mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 17:08:33 +08:00
Merge pull request #4877 from wucm667/fix/issue-4863-turnstile-invite-overlap
fix: show optional affiliate code on registration
This commit is contained in:
@@ -134,6 +134,27 @@
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<!-- Affiliate Invitation Code Input (Optional) -->
|
||||
<div v-else-if="affiliateEnabled" data-testid="affiliate-invitation-field">
|
||||
<label for="affiliate_code" class="input-label">
|
||||
{{ t('auth.invitationCodeLabel') }}
|
||||
<span class="ml-1 text-xs font-normal text-gray-400 dark:text-dark-500">({{ t('common.optional') }})</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3.5">
|
||||
<Icon name="key" size="md" class="text-gray-400 dark:text-dark-500" />
|
||||
</div>
|
||||
<input
|
||||
id="affiliate_code"
|
||||
v-model="formData.aff_code"
|
||||
type="text"
|
||||
:disabled="registrationActionDisabled"
|
||||
class="input pl-11"
|
||||
:placeholder="t('auth.invitationCodePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Promo Code Input (Optional) -->
|
||||
<div v-if="promoCodeEnabled">
|
||||
<label for="promo_code" class="input-label">
|
||||
@@ -183,7 +204,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Turnstile Widget -->
|
||||
<div v-if="turnstileEnabled && turnstileSiteKey">
|
||||
<div v-if="turnstileEnabled && turnstileSiteKey" data-testid="registration-turnstile">
|
||||
<TurnstileWidget
|
||||
ref="turnstileRef"
|
||||
:site-key="turnstileSiteKey"
|
||||
@@ -351,6 +372,7 @@ const registrationEnabled = ref<boolean>(true)
|
||||
const emailVerifyEnabled = ref<boolean>(false)
|
||||
const promoCodeEnabled = ref<boolean>(true)
|
||||
const invitationCodeEnabled = ref<boolean>(false)
|
||||
const affiliateEnabled = ref<boolean>(false)
|
||||
const turnstileEnabled = ref<boolean>(false)
|
||||
const turnstileSiteKey = ref<string>('')
|
||||
const siteName = ref<string>('Sub2API')
|
||||
@@ -459,6 +481,7 @@ onMounted(async () => {
|
||||
emailVerifyEnabled.value = settings.email_verify_enabled
|
||||
promoCodeEnabled.value = settings.promo_code_enabled
|
||||
invitationCodeEnabled.value = settings.invitation_code_enabled
|
||||
affiliateEnabled.value = settings.affiliate_enabled
|
||||
turnstileEnabled.value = settings.turnstile_enabled
|
||||
turnstileSiteKey.value = settings.turnstile_site_key || ''
|
||||
siteName.value = settings.site_name || 'Sub2API'
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import RegisterView from '@/views/auth/RegisterView.vue'
|
||||
|
||||
const { getPublicSettingsMock } = vi.hoisted(() => ({
|
||||
getPublicSettingsMock: vi.fn()
|
||||
}))
|
||||
|
||||
const publicSettings = {
|
||||
registration_enabled: true,
|
||||
email_verify_enabled: false,
|
||||
promo_code_enabled: false,
|
||||
invitation_code_enabled: false,
|
||||
affiliate_enabled: true,
|
||||
turnstile_enabled: true,
|
||||
turnstile_site_key: 'site-key',
|
||||
site_name: 'Sub2API',
|
||||
registration_email_suffix_whitelist: [],
|
||||
linuxdo_oauth_enabled: false,
|
||||
wechat_oauth_enabled: false,
|
||||
oidc_oauth_enabled: false,
|
||||
github_oauth_enabled: false,
|
||||
google_oauth_enabled: false
|
||||
}
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => ({ push: vi.fn() }),
|
||||
useRoute: () => ({ query: {} })
|
||||
}))
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
createI18n: () => ({
|
||||
global: {
|
||||
t: (key: string) => key
|
||||
}
|
||||
}),
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key,
|
||||
locale: { value: 'en' }
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/stores', () => ({
|
||||
useAuthStore: () => ({ register: vi.fn() }),
|
||||
useAppStore: () => ({
|
||||
showError: vi.fn(),
|
||||
showSuccess: vi.fn(),
|
||||
showWarning: vi.fn()
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/api/auth', async () => {
|
||||
const actual = await vi.importActual<typeof import('@/api/auth')>('@/api/auth')
|
||||
return {
|
||||
...actual,
|
||||
getPublicSettings: (...args: unknown[]) => getPublicSettingsMock(...args)
|
||||
}
|
||||
})
|
||||
|
||||
function mountRegister() {
|
||||
return mount(RegisterView, {
|
||||
global: {
|
||||
stubs: {
|
||||
AuthLayout: { template: '<div><slot /><slot name="footer" /></div>' },
|
||||
Icon: true,
|
||||
TurnstileWidget: { template: '<div data-testid="turnstile-widget" />' },
|
||||
LoginAgreementPrompt: true,
|
||||
EmailOAuthButtons: true,
|
||||
LinuxDoOAuthSection: true,
|
||||
WechatOAuthSection: true,
|
||||
OidcOAuthSection: true,
|
||||
RouterLink: true,
|
||||
transition: false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('RegisterView invitation layout', () => {
|
||||
beforeEach(() => {
|
||||
getPublicSettingsMock.mockReset()
|
||||
getPublicSettingsMock.mockResolvedValue(publicSettings)
|
||||
})
|
||||
|
||||
it('keeps the optional affiliate invitation field before Turnstile', async () => {
|
||||
const wrapper = mountRegister()
|
||||
await flushPromises()
|
||||
|
||||
const invitationField = wrapper.get('[data-testid="affiliate-invitation-field"]')
|
||||
const turnstile = wrapper.get('[data-testid="registration-turnstile"]')
|
||||
|
||||
expect(invitationField.get('input').attributes('id')).toBe('affiliate_code')
|
||||
expect(invitationField.text()).toContain('common.optional')
|
||||
expect(
|
||||
invitationField.element.compareDocumentPosition(turnstile.element) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('uses the mandatory invitation field without duplicating the affiliate field', async () => {
|
||||
getPublicSettingsMock.mockResolvedValueOnce({
|
||||
...publicSettings,
|
||||
invitation_code_enabled: true
|
||||
})
|
||||
|
||||
const wrapper = mountRegister()
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.find('[data-testid="affiliate-invitation-field"]').exists()).toBe(false)
|
||||
expect(wrapper.get('#invitation_code').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user