mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
test: clean up expected console output (#604)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
|
||||
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
|
||||
import { getActiveRequestsCount } from '@/utils/requestOptimizer'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/vue'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { createSite, createSiteDownloader } from '@tests/support/factories/site'
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
} from '@tests/support/msw/handlers/site'
|
||||
import { server } from '@tests/support/msw/server'
|
||||
import { renderWithProviders } from '@tests/support/render'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
done: vi.fn(),
|
||||
@@ -30,8 +31,10 @@ vi.mock('@/api/nprogress', () => ({
|
||||
startNProgress: mocks.start,
|
||||
}))
|
||||
|
||||
async function renderDialog(oper: 'add' | 'edit', siteid?: number) {
|
||||
async function renderDialog(oper: 'add' | 'edit', siteid?: number, downloaders = [createSiteDownloader()]) {
|
||||
const events = { close: vi.fn(), save: vi.fn() }
|
||||
const downloaderRequested = vi.fn()
|
||||
server.use(siteDownloadersHandler(downloaders, 200, downloaderRequested))
|
||||
const result = await renderWithProviders(SiteAddEditDialog, {
|
||||
props: {
|
||||
modelValue: true,
|
||||
@@ -42,22 +45,17 @@ async function renderDialog(oper: 'add' | 'edit', siteid?: number) {
|
||||
},
|
||||
global: { components: { VDialogCloseBtn: DialogCloseBtn } },
|
||||
})
|
||||
await waitFor(() => expect(downloaderRequested).toHaveBeenCalledOnce())
|
||||
await waitFor(() => expect(getActiveRequestsCount()).toBe(0))
|
||||
return { ...result, events }
|
||||
}
|
||||
|
||||
describe('SiteAddEditDialog', () => {
|
||||
beforeEach(() => {
|
||||
server.use(siteDownloadersHandler())
|
||||
})
|
||||
|
||||
it('creates a site with the entered form values and loaded downloader', async () => {
|
||||
const saved = vi.fn()
|
||||
server.use(
|
||||
siteDownloadersHandler([createSiteDownloader({ name: '下载器 A' })]),
|
||||
addSiteHandler({ success: true }, 200, saved),
|
||||
)
|
||||
server.use(addSiteHandler({ success: true }, 200, saved))
|
||||
const user = userEvent.setup()
|
||||
const { events } = await renderDialog('add')
|
||||
const { events } = await renderDialog('add', undefined, [createSiteDownloader({ name: '下载器 A' })])
|
||||
|
||||
await user.type(screen.getByLabelText('站点地址'), 'https://new.example.com/')
|
||||
await fireEvent.update(screen.getByLabelText('RSS地址'), 'https://new.example.com/rss')
|
||||
@@ -125,6 +123,7 @@ describe('SiteAddEditDialog', () => {
|
||||
})
|
||||
|
||||
it('restores progress and keeps the dialog open after an HTTP creation failure', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
server.use(addSiteHandler({ message: '服务异常', success: false }, 500))
|
||||
const user = userEvent.setup()
|
||||
const { events } = await renderDialog('add')
|
||||
@@ -135,6 +134,7 @@ describe('SiteAddEditDialog', () => {
|
||||
await waitFor(() => expect(mocks.done).toHaveBeenCalledOnce())
|
||||
expect(events.save).not.toHaveBeenCalled()
|
||||
expect(mocks.toastSuccess).not.toHaveBeenCalled()
|
||||
expect(consoleError).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('restores numeric flags and API mode when editing a rate-limited site', async () => {
|
||||
@@ -148,8 +148,8 @@ describe('SiteAddEditDialog', () => {
|
||||
render: 0,
|
||||
token: 'token',
|
||||
})
|
||||
server.use(siteDetailsHandler(site.id, site), siteDownloadersHandler([createSiteDownloader({ name: '下载器 A' })]))
|
||||
await renderDialog('edit', site.id)
|
||||
server.use(siteDetailsHandler(site.id, site))
|
||||
await renderDialog('edit', site.id, [createSiteDownloader({ name: '下载器 A' })])
|
||||
|
||||
expect(await screen.findByText(site.name)).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('使用代理访问')).toBeChecked()
|
||||
@@ -178,6 +178,7 @@ describe('SiteAddEditDialog', () => {
|
||||
['business', 200, { message: '不允许更新', success: false }, '限流站点 更新失败:不允许更新'],
|
||||
['HTTP', 500, { message: '服务异常', success: false }, '限流站点 更新失败!'],
|
||||
])('keeps the dialog open on %s update failure', async (_case, status, response, message) => {
|
||||
const consoleError = status === 500 ? vi.spyOn(console, 'error').mockImplementation(() => {}) : undefined
|
||||
const site = createSite({ name: '限流站点' })
|
||||
server.use(siteDetailsHandler(site.id, site), updateSiteHandler(response, status))
|
||||
const { events } = await renderDialog('edit', site.id)
|
||||
@@ -187,6 +188,7 @@ describe('SiteAddEditDialog', () => {
|
||||
await waitFor(() => expect(mocks.toastError).toHaveBeenCalledWith(message))
|
||||
expect(events.save).not.toHaveBeenCalled()
|
||||
expect(mocks.done).toHaveBeenCalledOnce()
|
||||
if (status === 500) expect(consoleError).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('emits close from the dialog close button', async () => {
|
||||
|
||||
@@ -90,6 +90,7 @@ describe('SiteCookieUpdateDialog', () => {
|
||||
['detail', { detail: '站点不存在' }, '站点不存在'],
|
||||
['message', { message: '认证服务异常' }, '认证服务异常'],
|
||||
])('shows an HTTP %s response and restores pending state', async (_case, body, message) => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
server.use(http.post('http://localhost/api/v1/site/cookie/701', () => HttpResponse.json(body, { status: 500 })))
|
||||
const user = userEvent.setup()
|
||||
await renderDialog()
|
||||
@@ -100,6 +101,7 @@ describe('SiteCookieUpdateDialog', () => {
|
||||
await waitFor(() => expect(mocks.toastError).toHaveBeenCalledWith(`Cookie 站点 更新失败:${message}`))
|
||||
expect(screen.getByRole('button', { name: '开始更新' })).toBeEnabled()
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
||||
expect(consoleError).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('disables the update action while the request is pending and restores it afterward', async () => {
|
||||
|
||||
@@ -83,15 +83,23 @@ describe('SiteImportDialog', () => {
|
||||
expect(screen.getByLabelText('选择文件')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it.each([
|
||||
['invalid JSON', brokenJsonFile(), '文件解析失败,请检查文件格式'],
|
||||
['non-array JSON', jsonFile({ name: 'single' }), '文件格式无效,请检查文件内容'],
|
||||
])('rejects %s', async (_case, file, message) => {
|
||||
it('rejects invalid JSON', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
await renderDialog()
|
||||
|
||||
await chooseFile(file)
|
||||
await chooseFile(brokenJsonFile())
|
||||
|
||||
await waitFor(() => expect(mocks.toastError).toHaveBeenCalledWith(message))
|
||||
await waitFor(() => expect(mocks.toastError).toHaveBeenCalledWith('文件解析失败,请检查文件格式'))
|
||||
expect(screen.getByLabelText('选择文件')).toBeInTheDocument()
|
||||
expect(consoleError).toHaveBeenCalledWith('Parse file error:', expect.any(SyntaxError))
|
||||
})
|
||||
|
||||
it('rejects non-array JSON', async () => {
|
||||
await renderDialog()
|
||||
|
||||
await chooseFile(jsonFile({ name: 'single' }))
|
||||
|
||||
await waitFor(() => expect(mocks.toastError).toHaveBeenCalledWith('文件格式无效,请检查文件内容'))
|
||||
expect(screen.getByLabelText('选择文件')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@@ -169,6 +177,7 @@ describe('SiteImportDialog', () => {
|
||||
})
|
||||
|
||||
it('reports partial success and preserves the HTTP error message', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
const sites = [createSite({ name: '成功站点' }), createSite({ name: '失败站点' })]
|
||||
let requestIndex = 0
|
||||
server.use(
|
||||
@@ -188,6 +197,7 @@ describe('SiteImportDialog', () => {
|
||||
expect(mocks.toastError).toHaveBeenCalledWith('导入完成,成功 1 个,失败 1 个')
|
||||
await fireEvent.click(screen.getByText('失败站点 - 错误详情'))
|
||||
expect(await screen.findByText('Request failed with status code 500')).toBeInTheDocument()
|
||||
expect(consoleError).toHaveBeenCalledWith('Import site 失败站点 failed:', expect.any(Error))
|
||||
})
|
||||
|
||||
it('does not start a request when every record is invalid', async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { MediaInfo, MediaSeason, NotExistMediaInfo } from '@/api/types'
|
||||
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
|
||||
import SubscribeSeasonDialog from '@/components/dialog/SubscribeSeasonDialog.vue'
|
||||
import type { SubscribeMode } from '@/composables/useMediaSubscribe'
|
||||
import { getActiveRequestsCount } from '@/utils/requestOptimizer'
|
||||
import { fireEvent, screen, waitFor, within } from '@testing-library/vue'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { createMediaInfo, createMediaSeason, createNotExistMediaInfo } from '@tests/support/factories/media'
|
||||
@@ -14,7 +15,7 @@ import {
|
||||
import { server } from '@tests/support/msw/server'
|
||||
import { renderWithProviders } from '@tests/support/render'
|
||||
import { flushPromises } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
/** 季订阅弹窗测试属性。 */
|
||||
interface SeasonDialogProps {
|
||||
@@ -102,9 +103,8 @@ async function settleRequests() {
|
||||
}
|
||||
|
||||
describe('SubscribeSeasonDialog', () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
afterEach(async () => {
|
||||
await waitFor(() => expect(getActiveRequestsCount()).toBe(0))
|
||||
})
|
||||
|
||||
it('loads the default TMDB seasons once with exact requests and renders season states', async () => {
|
||||
@@ -250,7 +250,9 @@ describe('SubscribeSeasonDialog', () => {
|
||||
},
|
||||
'tmdb:source-7306',
|
||||
],
|
||||
] as const)('uses the %s media identifier without requesting TMDB groups', async (_label, overrides, mediaId) => {
|
||||
] as const)('uses the %s media identifier without requesting TMDB groups', async (label, overrides, mediaId) => {
|
||||
const consoleWarn =
|
||||
label === 'source-only TMDB' ? vi.spyOn(console, 'warn').mockImplementation(() => {}) : undefined
|
||||
const media = createTvMedia(overrides)
|
||||
const requested = vi.fn()
|
||||
server.use(
|
||||
@@ -264,6 +266,7 @@ describe('SubscribeSeasonDialog', () => {
|
||||
await settleRequests()
|
||||
expect(requested).toHaveBeenCalledOnce()
|
||||
expect(requested.mock.calls[0][0].searchParams.get('mediaid')).toBe(mediaId)
|
||||
if (label === 'source-only TMDB') expect(consoleWarn).toHaveBeenCalledWith('tmdbid is not set or is empty')
|
||||
})
|
||||
|
||||
it.each([
|
||||
@@ -417,6 +420,7 @@ describe('SubscribeSeasonDialog', () => {
|
||||
})
|
||||
|
||||
it('exits Loading after the default season request fails', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
const media = createTvMedia({ tmdb_id: 7310 })
|
||||
const requested = vi.fn()
|
||||
server.use(
|
||||
@@ -431,9 +435,11 @@ describe('SubscribeSeasonDialog', () => {
|
||||
|
||||
expect(document.querySelector('.initial-loading-container')).not.toBeInTheDocument()
|
||||
expect(screen.getByText(`${media.title} 未查询到季集信息`)).toBeInTheDocument()
|
||||
expect(consoleError).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('keeps season selection usable when the missing-state request fails', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
const media = createTvMedia({ tmdb_id: 7313 })
|
||||
const missingRequested = vi.fn()
|
||||
server.use(
|
||||
@@ -452,9 +458,11 @@ describe('SubscribeSeasonDialog', () => {
|
||||
|
||||
expect(events.subscribe).toHaveBeenCalledOnce()
|
||||
expect(events.subscribe.mock.calls[0][1]).toEqual({})
|
||||
expect(consoleError).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('keeps season selection usable when optional episode groups fail to load', async () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
const media = createTvMedia({ tmdb_id: 7314 })
|
||||
const groupsRequested = vi.fn()
|
||||
server.use(
|
||||
@@ -473,6 +481,7 @@ describe('SubscribeSeasonDialog', () => {
|
||||
|
||||
expect(events.subscribe).toHaveBeenCalledOnce()
|
||||
expect(screen.getByRole('button', { name: /^默认/ })).toBeInTheDocument()
|
||||
expect(consoleError).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('renders the successful empty state and emits close without submitting', async () => {
|
||||
|
||||
Reference in New Issue
Block a user