test(subscribe): cover subscription management flows (#530)

This commit is contained in:
InfinityPacer
2026-07-16 17:37:43 +08:00
committed by GitHub
parent 9900042bc3
commit 8a069db55e
12 changed files with 1534 additions and 30 deletions

View File

@@ -0,0 +1,181 @@
import type { DownloaderConf, FilterRuleGroup, Site, Subscribe, TransferDirectoryConf } from '@/api/types'
import { HttpResponse, http, type JsonBodyType, type RequestHandler } from 'msw'
const API_BASE_URL = 'http://localhost/api/v1/'
export type SubscribeMediaType = '电影' | '电视剧'
export interface SubscribeMutationResponse {
success: boolean
data?: Record<string, unknown>
message?: string
}
export const subscribeApiUrls = {
create: new URL('subscribe/', API_BASE_URL).href,
defaultConfig: (type: SubscribeMediaType, writable = false) =>
new URL(
`system/setting/${writable ? '' : 'public/'}${type === '电影' ? 'DefaultMovieSubscribeConfig' : 'DefaultTvSubscribeConfig'}`,
API_BASE_URL,
).href,
deleteById: (id: number) => new URL(`subscribe/${id}`, API_BASE_URL).href,
deleteByMedia: (mediaId: string) => new URL(`subscribe/media/${mediaId}`, API_BASE_URL).href,
details: (id: number) => new URL(`subscribe/${id}`, API_BASE_URL).href,
directories: new URL('system/setting/public/Directories', API_BASE_URL).href,
downloaders: new URL('download/clients', API_BASE_URL).href,
episodeGroups: (tmdbId: number) => new URL(`media/groups/${tmdbId}`, API_BASE_URL).href,
filterRuleGroups: new URL('system/setting/UserFilterRuleGroups', API_BASE_URL).href,
queryByMedia: (mediaId: string) => new URL(`subscribe/media/${mediaId}`, API_BASE_URL).href,
sites: new URL('site/rss', API_BASE_URL).href,
update: new URL('subscribe/', API_BASE_URL).href,
}
function jsonResponse(body: JsonBodyType, status: number) {
return HttpResponse.json(body, { status })
}
export function createSubscribeHandler(
response: SubscribeMutationResponse = { data: { id: 1 }, success: true },
status = 200,
onCreate: (payload: Record<string, unknown>) => void = () => {},
) {
return http.post(subscribeApiUrls.create, async ({ request }) => {
const payload = (await request.json()) as Record<string, unknown>
onCreate(payload)
return jsonResponse(response, status)
})
}
export function updateSubscribeHandler(
response: SubscribeMutationResponse = { success: true },
status = 200,
onUpdate: (payload: Record<string, unknown>) => void = () => {},
) {
return http.put(subscribeApiUrls.update, async ({ request }) => {
const payload = (await request.json()) as Record<string, unknown>
onUpdate(payload)
return jsonResponse(response, status)
})
}
export function querySubscribeByMediaHandler(
mediaId: string,
subscribe: Partial<Subscribe>,
status = 200,
onRequest: (url: URL) => void = () => {},
) {
return http.get(subscribeApiUrls.queryByMedia(mediaId), ({ request }) => {
onRequest(new URL(request.url))
return jsonResponse(subscribe as JsonBodyType, status)
})
}
export function deleteSubscribeByMediaHandler(
mediaId: string,
response: SubscribeMutationResponse = { success: true },
status = 200,
onRequest: (url: URL) => void = () => {},
) {
return http.delete(subscribeApiUrls.deleteByMedia(mediaId), ({ request }) => {
onRequest(new URL(request.url))
return jsonResponse(response, status)
})
}
export function subscribeDetailsHandler(id: number, subscribe: Subscribe, status = 200, onRequest: () => void = () => {}) {
return http.get(subscribeApiUrls.details(id), () => {
onRequest()
return jsonResponse(subscribe as unknown as JsonBodyType, status)
})
}
export function deleteSubscribeByIdHandler(
id: number,
response: SubscribeMutationResponse = { success: true },
status = 200,
onRequest: () => void = () => {},
) {
return http.delete(subscribeApiUrls.deleteById(id), () => {
onRequest()
return jsonResponse(response, status)
})
}
export function defaultSubscribeConfigHandler(
type: SubscribeMediaType,
config: Partial<Subscribe>,
status = 200,
onRequest: () => void = () => {},
) {
return http.get(subscribeApiUrls.defaultConfig(type), () => {
onRequest()
return jsonResponse({ data: { value: config }, success: status < 400 }, status)
})
}
export function saveDefaultSubscribeConfigHandler(
type: SubscribeMediaType,
response: SubscribeMutationResponse = { success: true },
status = 200,
onSave: (payload: Record<string, unknown>) => void = () => {},
) {
return http.post(subscribeApiUrls.defaultConfig(type, true), async ({ request }) => {
const payload = (await request.json()) as Record<string, unknown>
onSave(payload)
return jsonResponse(response, status)
})
}
export interface SubscribeDialogOptions {
directories?: TransferDirectoryConf[]
downloaders?: DownloaderConf[]
episodeGroups?: Record<string, unknown>[]
filterRuleGroups?: FilterRuleGroup[]
onDirectories?: () => void
onDownloaders?: () => void
onEpisodeGroups?: () => void
onFilterRuleGroups?: () => void
onSites?: () => void
sites?: Site[]
tmdbId?: number
}
/** 为编辑弹窗提供彼此独立、可按测试覆盖的选项接口。 */
export function subscribeDialogOptionHandlers(options: SubscribeDialogOptions = {}): RequestHandler[] {
const {
directories = [],
downloaders = [],
episodeGroups = [],
filterRuleGroups = [],
onDirectories = () => {},
onDownloaders = () => {},
onEpisodeGroups = () => {},
onFilterRuleGroups = () => {},
onSites = () => {},
sites = [],
tmdbId = 1,
} = options
return [
http.get(subscribeApiUrls.sites, () => {
onSites()
return jsonResponse(sites as unknown as JsonBodyType, 200)
}),
http.get(subscribeApiUrls.downloaders, () => {
onDownloaders()
return jsonResponse(downloaders as unknown as JsonBodyType, 200)
}),
http.get(subscribeApiUrls.directories, () => {
onDirectories()
return jsonResponse({ data: { value: directories }, success: true }, 200)
}),
http.get(subscribeApiUrls.filterRuleGroups, () => {
onFilterRuleGroups()
return jsonResponse({ data: { value: filterRuleGroups }, success: true }, 200)
}),
http.get(subscribeApiUrls.episodeGroups(tmdbId), () => {
onEpisodeGroups()
return jsonResponse(episodeGroups as unknown as JsonBodyType, 200)
}),
]
}