mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
Merge pull request #6764 from ouchihao/codex/issue-1499-select-failed-token-refresh
fix(accounts): keep failed token refresh accounts selected
This commit is contained in:
@@ -1891,10 +1891,13 @@ const handleBulkResetStatus = async () => {
|
||||
}
|
||||
const handleBulkRefreshToken = async () => {
|
||||
if (!confirm(t('common.confirm'))) return
|
||||
const accountIds = [...selIds.value]
|
||||
try {
|
||||
const result = await adminAPI.accounts.batchRefresh(selIds.value)
|
||||
const result = await adminAPI.accounts.batchRefresh(accountIds)
|
||||
if (result.failed > 0) {
|
||||
appStore.showError(t('admin.accounts.bulkActions.partialSuccess', { success: result.success, failed: result.failed }))
|
||||
const failedIds = result.errors?.map(error => error.account_id) ?? []
|
||||
setSelectedIds(failedIds.length > 0 ? failedIds : accountIds)
|
||||
} else {
|
||||
appStore.showSuccess(t('admin.accounts.bulkActions.refreshTokenSuccess', { count: result.success }))
|
||||
clearSelection()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
|
||||
import AccountsView from '../AccountsView.vue'
|
||||
@@ -6,6 +6,7 @@ import AccountsView from '../AccountsView.vue'
|
||||
const {
|
||||
listAccounts,
|
||||
listWithEtag,
|
||||
batchRefresh,
|
||||
getBatchTodayStats,
|
||||
getUpstreamBillingProbeSettings,
|
||||
getAllProxies,
|
||||
@@ -14,6 +15,7 @@ const {
|
||||
} = vi.hoisted(() => ({
|
||||
listAccounts: vi.fn(),
|
||||
listWithEtag: vi.fn(),
|
||||
batchRefresh: vi.fn(),
|
||||
getBatchTodayStats: vi.fn(),
|
||||
getUpstreamBillingProbeSettings: vi.fn(),
|
||||
getAllProxies: vi.fn(),
|
||||
@@ -30,7 +32,7 @@ vi.mock('@/api/admin', () => ({
|
||||
getUpstreamBillingProbeSettings,
|
||||
batchDelete: vi.fn(),
|
||||
batchClearError: vi.fn(),
|
||||
batchRefresh: vi.fn(),
|
||||
batchRefresh,
|
||||
bulkUpdate: vi.fn()
|
||||
},
|
||||
proxies: {
|
||||
@@ -79,7 +81,7 @@ const makeAccounts = (count: number) => Array.from({ length: count }, (_, index)
|
||||
|
||||
const AccountBulkActionsBarStub = {
|
||||
props: ['selectedIds', 'totalResults', 'selectingAll', 'allResultsSelected'],
|
||||
emits: ['select-all-results', 'select-page', 'clear'],
|
||||
emits: ['select-all-results', 'select-page', 'clear', 'refresh-token'],
|
||||
template: `
|
||||
<div>
|
||||
<span data-test="selected-count">{{ selectedIds.length }}</span>
|
||||
@@ -88,6 +90,7 @@ const AccountBulkActionsBarStub = {
|
||||
<button data-test="select-page" @click="$emit('select-page')">select page</button>
|
||||
<button data-test="select-all-results" @click="$emit('select-all-results')">select all</button>
|
||||
<button data-test="clear" @click="$emit('clear')">clear</button>
|
||||
<button data-test="refresh-token" @click="$emit('refresh-token')">refresh token</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -104,7 +107,10 @@ const mountView = () => mount(AccountsView, {
|
||||
TablePageLayout: {
|
||||
template: '<div><slot name="filters" /><slot name="table" /><slot name="pagination" /></div>'
|
||||
},
|
||||
DataTable: { props: ['data'], template: '<div data-test="data-table"></div>' },
|
||||
DataTable: {
|
||||
props: ['data'],
|
||||
template: '<div data-test="data-table"><div v-for="row in data" :key="row.id"><slot name="cell-select" :row="row" /></div></div>'
|
||||
},
|
||||
Pagination: true,
|
||||
ConfirmDialog: true,
|
||||
AccountTableActions: { template: '<div><slot name="beforeCreate" /><slot name="after" /></div>' },
|
||||
@@ -139,6 +145,7 @@ describe('admin AccountsView select all filtered results', () => {
|
||||
localStorage.clear()
|
||||
listAccounts.mockReset()
|
||||
listWithEtag.mockReset()
|
||||
batchRefresh.mockReset()
|
||||
getBatchTodayStats.mockReset()
|
||||
getUpstreamBillingProbeSettings.mockReset()
|
||||
getAllProxies.mockReset()
|
||||
@@ -156,6 +163,38 @@ describe('admin AccountsView select all filtered results', () => {
|
||||
getAllGroups.mockResolvedValue([])
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ name: 'keeps only failed accounts selected', result: { total: 3, success: 2, failed: 1, errors: [{ account_id: 2, error: 'no refresh token available' }] }, expectedIds: [2] },
|
||||
{ name: 'clears the selection after every account succeeds', result: { total: 3, success: 3, failed: 0 }, expectedIds: [] },
|
||||
{ name: 'keeps the original selection when failure details are missing', result: { total: 3, success: 2, failed: 1 }, expectedIds: [1, 2, 3] },
|
||||
])('$name after a batch token refresh and table reload', async ({ result, expectedIds }) => {
|
||||
listAccounts.mockResolvedValue({ items: makeAccounts(3), total: 3, page: 1, page_size: 20, pages: 1 })
|
||||
batchRefresh.mockResolvedValue(result)
|
||||
vi.spyOn(window, 'confirm').mockReturnValue(true)
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
await wrapper.get('[data-test="select-page"]').trigger('click')
|
||||
await wrapper.get('[data-test="refresh-token"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(batchRefresh).toHaveBeenCalledWith([1, 2, 3])
|
||||
expect(listAccounts).toHaveBeenCalledTimes(2)
|
||||
expect(wrapper.getComponent(AccountBulkActionsBarStub).props('selectedIds')).toEqual(expectedIds)
|
||||
expect(wrapper.findAll<HTMLInputElement>('[data-test="data-table"] input').map(input => input.element.checked))
|
||||
.toEqual([1, 2, 3].map(id => expectedIds.includes(id)))
|
||||
if (result.failed > 0) {
|
||||
expect(showError).toHaveBeenCalledWith('admin.accounts.bulkActions.partialSuccess')
|
||||
await wrapper.get('[data-test="refresh-token"]').trigger('click')
|
||||
await flushPromises()
|
||||
expect(batchRefresh).toHaveBeenLastCalledWith(expectedIds)
|
||||
}
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('selects all matching IDs in one commit and clears the selection when filters change', async () => {
|
||||
const allAccounts = makeAccounts(45)
|
||||
listAccounts.mockImplementation(async (_page: number, pageSize: number) => {
|
||||
|
||||
Reference in New Issue
Block a user