mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-10 02:06:44 +08:00
fix: separate auto-update and dev tracking settings for clarity by jxxghp
This commit is contained in:
@@ -2651,6 +2651,10 @@ export interface SystemUpdateItemStatus {
|
||||
|
||||
/** 后端主程序与站点资源后台更新状态机快照。 */
|
||||
export interface SystemUpdateStatus {
|
||||
/** 当前自动检查开关;由后端实时返回,避免旧缓存继续触发提醒。 */
|
||||
auto_update?: boolean
|
||||
/** 站点资源单独控制自动检查和提醒。 */
|
||||
auto_update_resource?: boolean
|
||||
state: SystemUpdateState
|
||||
current_version: string
|
||||
version?: string | null
|
||||
|
||||
@@ -70,6 +70,9 @@ const updateItems = computed<SystemUpdateItemStatus[]>(() => {
|
||||
|
||||
const visibleItems = computed(() =>
|
||||
updateItems.value.filter(item => {
|
||||
// 关闭自动检查后隐藏版本提醒,手动发起的下载、安装和失败反馈仍可见。
|
||||
const enabled = item.type === 'resources' ? status.value?.auto_update_resource : status.value?.auto_update
|
||||
if (['available', 'ready'].includes(item.state) && enabled !== true) return false
|
||||
if (!['available', 'downloading', 'ready', 'installing', 'failed'].includes(item.state)) return false
|
||||
return !['available', 'ready'].includes(item.state) || !isCurrentVersionSuppressed(item)
|
||||
}),
|
||||
|
||||
@@ -57,6 +57,8 @@ vi.mock('vue-i18n', async importOriginal => ({
|
||||
}))
|
||||
|
||||
const availableStatus: SystemUpdateStatus = {
|
||||
auto_update: true,
|
||||
auto_update_resource: true,
|
||||
state: 'available',
|
||||
current_version: 'v3.0.0',
|
||||
version: 'v3.1.0',
|
||||
@@ -77,6 +79,43 @@ describe('SystemUpdatePrompt', () => {
|
||||
mocks.footerDockHeight!.value = null
|
||||
})
|
||||
|
||||
it.each(['available', 'ready'] as const)(
|
||||
'hides cached %s reminders when automatic checks are disabled',
|
||||
async state => {
|
||||
mocks.updateStatus!.value = { ...availableStatus, state, auto_update: false }
|
||||
await renderWithProviders(SystemUpdatePrompt, { props: { enabled: true } })
|
||||
expect(document.querySelector('.system-update-prompt')).not.toBeInTheDocument()
|
||||
|
||||
mocks.updateStatus!.value = { ...availableStatus, state, auto_update: true }
|
||||
await waitFor(() => expect(document.querySelector('.system-update-prompt')).toBeInTheDocument())
|
||||
mocks.updateStatus!.value = { ...availableStatus, state, auto_update: false }
|
||||
await waitFor(() => expect(document.querySelector('.system-update-prompt')).not.toBeInTheDocument())
|
||||
},
|
||||
)
|
||||
|
||||
it('keeps manually started download progress visible with automatic checks disabled', async () => {
|
||||
mocks.updateStatus!.value = { ...availableStatus, state: 'downloading', auto_update: false, progress: 25 }
|
||||
await renderWithProviders(SystemUpdatePrompt, { props: { enabled: true } })
|
||||
expect(screen.getByText('25%')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows only resource reminders when only automatic resource updates are enabled', async () => {
|
||||
mocks.updateStatus!.value = {
|
||||
...availableStatus,
|
||||
auto_update: false,
|
||||
auto_update_resource: true,
|
||||
updates: [
|
||||
{ ...availableStatus, type: 'application' },
|
||||
{ ...availableStatus, type: 'resources', auth_version: '3.1.0' },
|
||||
],
|
||||
}
|
||||
await renderWithProviders(SystemUpdatePrompt, { props: { enabled: true } })
|
||||
expect(screen.queryByText('systemUpdate.applicationAvailableTitle')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('systemUpdate.resourcesAvailableTitle')).toBeInTheDocument()
|
||||
mocks.updateStatus!.value = { ...mocks.updateStatus!.value, auto_update_resource: false }
|
||||
await waitFor(() => expect(document.querySelector('.system-update-prompt')).not.toBeInTheDocument())
|
||||
})
|
||||
|
||||
it('asks an administrator to start the background download', async () => {
|
||||
mocks.post.mockResolvedValue({
|
||||
...availableStatus,
|
||||
|
||||
@@ -2989,6 +2989,9 @@ export default {
|
||||
imageProxyAllowedPrivateRangeAdd: 'Add CIDR, e.g.: 198.18.0.0/15',
|
||||
proxyHost: 'Proxy Server',
|
||||
proxyHostHint: 'Set proxy server address, support: http(s), socks5, socks5h, etc.',
|
||||
moviePilotAutoUpdate: 'Automatically check for updates',
|
||||
moviePilotAutoUpdateHint:
|
||||
'Check for application updates every 6 hours and show reminders; confirm downloads and installation manually',
|
||||
moviePilotDevUpdate: 'Track Dev builds',
|
||||
moviePilotDevUpdateHint: 'Update to the latest code on the current v3 development branch at every restart',
|
||||
autoUpdateResource: 'Auto Update Resource',
|
||||
|
||||
@@ -2924,6 +2924,8 @@ export default {
|
||||
imageProxyAllowedPrivateRangeAdd: '添加 CIDR,如:198.18.0.0/15',
|
||||
proxyHost: '代理服务器',
|
||||
proxyHostHint: '设置代理服务器地址,支持:http(s)、socks5、socks5h 等协议',
|
||||
moviePilotAutoUpdate: '自动检查版本更新',
|
||||
moviePilotAutoUpdateHint: '每 6 小时检查主程序版本更新并提示,下载和安装需手动确认',
|
||||
moviePilotDevUpdate: '跟踪 Dev 开发版',
|
||||
moviePilotDevUpdateHint: '每次重启时更新到当前 v3 开发分支的最新代码',
|
||||
autoUpdateResource: '自动更新站点资源',
|
||||
|
||||
@@ -2897,6 +2897,8 @@ export default {
|
||||
imageProxyAllowedPrivateRangeAdd: '添加 CIDR,如:198.18.0.0/15',
|
||||
proxyHost: '代理服務器',
|
||||
proxyHostHint: '設置代理服務器地址,支持:http(s)、socks5、socks5h 等協議',
|
||||
moviePilotAutoUpdate: '自動檢查版本更新',
|
||||
moviePilotAutoUpdateHint: '每 6 小時檢查主程式版本更新並提示,下載和安裝需手動確認',
|
||||
moviePilotDevUpdate: '追蹤 Dev 開發版',
|
||||
moviePilotDevUpdateHint: '每次重新啟動時更新到目前 v3 開發分支的最新程式碼',
|
||||
autoUpdateResource: '自動更新站點資源',
|
||||
|
||||
@@ -99,6 +99,7 @@ const SystemSettings = ref<any>({
|
||||
DB_WAL_ENABLE: false,
|
||||
AUTO_UPDATE_RESOURCE: true,
|
||||
MOVIEPILOT_AUTO_UPDATE: false,
|
||||
MOVIEPILOT_UPDATE_DEV: false,
|
||||
DATA_CLEANUP_ENABLE: false,
|
||||
DATA_CLEANUP_MESSAGE_DAYS: 90,
|
||||
DATA_CLEANUP_DOWNLOAD_HISTORY_DAYS: 180,
|
||||
@@ -1117,13 +1118,6 @@ function onMediaServerChange(mediaserver: MediaServerConf, name: string) {
|
||||
if (index !== -1) mediaServers.value[index] = mediaserver
|
||||
}
|
||||
|
||||
const moviePilotDevUpdate = computed({
|
||||
get: () => SystemSettings.value.Advanced.MOVIEPILOT_AUTO_UPDATE === 'dev',
|
||||
set: enabled => {
|
||||
SystemSettings.value.Advanced.MOVIEPILOT_AUTO_UPDATE = enabled ? 'dev' : 'false'
|
||||
},
|
||||
})
|
||||
|
||||
// Fanart语言多选处理
|
||||
const fanartLanguageSelection = computed({
|
||||
get: () => {
|
||||
@@ -2131,7 +2125,15 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VSwitch
|
||||
v-model="moviePilotDevUpdate"
|
||||
v-model="SystemSettings.Advanced.MOVIEPILOT_AUTO_UPDATE"
|
||||
:label="t('setting.system.moviePilotAutoUpdate')"
|
||||
:hint="t('setting.system.moviePilotAutoUpdateHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VSwitch
|
||||
v-model="SystemSettings.Advanced.MOVIEPILOT_UPDATE_DEV"
|
||||
:label="t('setting.system.moviePilotDevUpdate')"
|
||||
:hint="t('setting.system.moviePilotDevUpdateHint')"
|
||||
persistent-hint
|
||||
|
||||
@@ -1006,7 +1006,30 @@ describe('AccountSettingSystem', () => {
|
||||
expect(mocks.toastSuccess).toHaveBeenCalledWith('高级设置保存成功')
|
||||
})
|
||||
|
||||
it('round-trips all advanced system switches and enables only the Dev update mode', async () => {
|
||||
it.each([
|
||||
[false, false],
|
||||
[false, true],
|
||||
[true, false],
|
||||
[true, true],
|
||||
])('loads and saves independent update switches (%s, %s)', async (autoUpdate, updateDev) => {
|
||||
systemEnv.MOVIEPILOT_AUTO_UPDATE = autoUpdate
|
||||
systemEnv.MOVIEPILOT_UPDATE_DEV = updateDev
|
||||
await renderSettings()
|
||||
const dialog = await openAdvancedTab('系统')
|
||||
const autoSwitch = dialog.getByLabelText('自动检查版本更新')
|
||||
const devSwitch = dialog.getByLabelText('跟踪 Dev 开发版')
|
||||
expect((autoSwitch as HTMLInputElement).checked).toBe(autoUpdate)
|
||||
expect((devSwitch as HTMLInputElement).checked).toBe(updateDev)
|
||||
await fireEvent.click(autoSwitch)
|
||||
expect((devSwitch as HTMLInputElement).checked).toBe(updateDev)
|
||||
await fireEvent.click(dialog.getByRole('button', { name: '保存' }))
|
||||
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument())
|
||||
expect(findPost('system/env')?.[1]).toEqual(
|
||||
expect.objectContaining({ MOVIEPILOT_AUTO_UPDATE: !autoUpdate, MOVIEPILOT_UPDATE_DEV: updateDev }),
|
||||
)
|
||||
})
|
||||
|
||||
it('round-trips all advanced system switches with independent boolean update settings', async () => {
|
||||
await renderSettings()
|
||||
const dialog = await openAdvancedTab('系统')
|
||||
for (const label of [
|
||||
@@ -1018,6 +1041,7 @@ describe('AccountSettingSystem', () => {
|
||||
'分享工作流数据',
|
||||
'大内存模式',
|
||||
'数据库WAL模式',
|
||||
'自动检查版本更新',
|
||||
'跟踪 Dev 开发版',
|
||||
'自动更新站点资源',
|
||||
]) {
|
||||
@@ -1033,7 +1057,8 @@ describe('AccountSettingSystem', () => {
|
||||
BIG_MEMORY_MODE: true,
|
||||
DB_WAL_ENABLE: true,
|
||||
GLOBAL_IMAGE_CACHE: true,
|
||||
MOVIEPILOT_AUTO_UPDATE: 'dev',
|
||||
MOVIEPILOT_AUTO_UPDATE: true,
|
||||
MOVIEPILOT_UPDATE_DEV: true,
|
||||
PLUGIN_STATISTIC_SHARE: false,
|
||||
SUBSCRIBE_STATISTIC_SHARE: false,
|
||||
USAGE_STATISTIC_SHARE: false,
|
||||
|
||||
Reference in New Issue
Block a user