Merge pull request #7024 from ouchihao/codex/fix-subscription-assignment-selection

fix(subscriptions): clear assignment user before search debounce
This commit is contained in:
Wesley Liddick
2026-09-12 19:24:00 +08:00
committed by GitHub
2 changed files with 55 additions and 9 deletions
@@ -1126,6 +1126,11 @@ const clearFilterUser = () => {
// User search with debounce
const debounceSearchUsers = () => {
// Invalidate the assignment target before the debounced search runs.
if (selectedUser.value && userSearchKeyword.value.trim() !== selectedUser.value.email) {
selectedUser.value = null
assignForm.user_id = null
}
if (userSearchTimeout) {
clearTimeout(userSearchTimeout)
}
@@ -1135,12 +1140,6 @@ const debounceSearchUsers = () => {
const searchUsers = async () => {
const keyword = userSearchKeyword.value.trim()
// Clear selection if user modified the search keyword
if (selectedUser.value && keyword !== selectedUser.value.email) {
selectedUser.value = null
assignForm.user_id = null
}
if (!keyword) {
userSearchResults.value = []
return
@@ -4,8 +4,10 @@ import { defineComponent } from 'vue'
import SubscriptionsView from '../SubscriptionsView.vue'
const { listSubscriptions, getAllGroups, listUsers, searchUsageUsers } = vi.hoisted(() => ({
const { listSubscriptions, assignSubscription, getAllGroups, listUsers, searchUsageUsers, showError } = vi.hoisted(() => ({
listSubscriptions: vi.fn(),
assignSubscription: vi.fn(),
showError: vi.fn(),
getAllGroups: vi.fn(),
listUsers: vi.fn(),
searchUsageUsers: vi.fn()
@@ -13,7 +15,7 @@ const { listSubscriptions, getAllGroups, listUsers, searchUsageUsers } = vi.hois
vi.mock('@/api/admin', () => ({
adminAPI: {
subscriptions: { list: listSubscriptions },
subscriptions: { list: listSubscriptions, assign: assignSubscription },
groups: { getAll: getAllGroups },
users: { list: listUsers },
usage: { searchUsers: searchUsageUsers }
@@ -22,7 +24,7 @@ vi.mock('@/api/admin', () => ({
vi.mock('@/stores/app', () => ({
useAppStore: () => ({
showError: vi.fn(),
showError,
showSuccess: vi.fn()
})
}))
@@ -80,6 +82,7 @@ describe('admin subscription users', () => {
total: 1,
pages: 1
})
assignSubscription.mockResolvedValue({})
getAllGroups.mockResolvedValue([])
listUsers.mockResolvedValue({
items: [{ id: 42, email: 'reader@example.com' }],
@@ -143,6 +146,50 @@ describe('admin subscription users', () => {
}
})
it.each(['another', ''])('clears the assignment user immediately when input changes to %j', async (keyword) => {
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] })
const wrapper = mountView()
try {
await flushPromises()
await wrapper.findAll('button')
.find((button) => button.text() === 'admin.subscriptions.assignSubscription')!
.trigger('click')
const form = wrapper.get('#assign-subscription-form')
form.getComponent({ name: 'Select' }).vm.$emit('update:modelValue', 3)
const search = wrapper.get('[data-assign-user-search] input')
await search.trigger('focus')
await search.setValue('reader')
await vi.advanceTimersByTimeAsync(300)
await flushPromises()
await wrapper.get('[data-assign-user-search] button').trigger('click')
await search.setValue(keyword)
await form.trigger('submit')
await flushPromises()
expect(assignSubscription).not.toHaveBeenCalled()
expect(showError).toHaveBeenCalledWith('admin.subscriptions.pleaseSelectUser')
expect(listUsers).toHaveBeenCalledTimes(1)
listUsers.mockResolvedValue({ items: [{ id: 84, email: 'another@example.com' }] })
await search.trigger('focus')
await search.setValue('another')
await vi.advanceTimersByTimeAsync(300)
await flushPromises()
await wrapper.get('[data-assign-user-search] button').trigger('click')
await form.trigger('submit')
await flushPromises()
expect(assignSubscription).toHaveBeenCalledTimes(1)
expect(assignSubscription).toHaveBeenCalledWith({
user_id: 84, group_id: 3, validity_days: 30
})
} finally {
wrapper.unmount()
vi.useRealTimers()
}
})
it('keeps deleted users available when filtering subscription history', async () => {
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] })
const wrapper = mountView()