test: cover renderer state helpers
This commit is contained in:
@@ -1,21 +1,33 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
function parseProductionIds(input: string): string[] {
|
interface ValidationSyncApi {
|
||||||
|
clearSharedProductionIds: () => Promise<unknown>
|
||||||
|
setSharedProductionIds: (ids: string[]) => Promise<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseProductionIds(input: string): string[] {
|
||||||
return input
|
return input
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((line) => line.trim())
|
.map((line) => line.trim())
|
||||||
.filter((line) => line.length > 0)
|
.filter((line) => line.length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncSharedProductionIds(input: string): Promise<void> {
|
export async function syncSharedProductionIdsWithApi(
|
||||||
|
input: string,
|
||||||
|
validationApi: ValidationSyncApi
|
||||||
|
): Promise<void> {
|
||||||
const productionIds = parseProductionIds(input)
|
const productionIds = parseProductionIds(input)
|
||||||
|
|
||||||
if (productionIds.length === 0) {
|
if (productionIds.length === 0) {
|
||||||
await window.electron.validation.clearSharedProductionIds()
|
await validationApi.clearSharedProductionIds()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await window.electron.validation.setSharedProductionIds(productionIds)
|
await validationApi.setSharedProductionIds(productionIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncSharedProductionIds(input: string): Promise<void> {
|
||||||
|
await syncSharedProductionIdsWithApi(input, window.electron.validation)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSharedProductionIds(orderNumbers: string, debounceMs = 300) {
|
export function useSharedProductionIds(orderNumbers: string, debounceMs = 300) {
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { startTransition, useEffect, useRef, useState } from 'react'
|
import { startTransition, useEffect, useRef, useState } from 'react'
|
||||||
import type { UpdateDialogCatalog, UpdateRelease } from '../../../main/types/update.types'
|
import type { UpdateDialogCatalog, UpdateRelease } from '../../../main/types/update.types'
|
||||||
|
|
||||||
function releaseKey(release: UpdateRelease): string {
|
export function releaseKey(release: UpdateRelease): string {
|
||||||
return `${release.channel}:${release.version}`
|
return `${release.channel}:${release.version}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInitialSelectedRelease(catalog: UpdateDialogCatalog | null): UpdateRelease | undefined {
|
export function getInitialSelectedRelease(
|
||||||
|
catalog: UpdateDialogCatalog | null
|
||||||
|
): UpdateRelease | undefined {
|
||||||
if (!catalog || catalog.mode === 'disabled') {
|
if (!catalog || catalog.mode === 'disabled') {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|||||||
40
tests/unit/use-shared-production-ids.test.ts
Normal file
40
tests/unit/use-shared-production-ids.test.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
import {
|
||||||
|
parseProductionIds,
|
||||||
|
syncSharedProductionIdsWithApi
|
||||||
|
} from '../../src/renderer/src/hooks/useSharedProductionIds'
|
||||||
|
|
||||||
|
describe('useSharedProductionIds helpers', () => {
|
||||||
|
it('parses trimmed non-empty production ids', () => {
|
||||||
|
expect(parseProductionIds(' 22A1 \n\nSC70202602120085\n ')).toEqual([
|
||||||
|
'22A1',
|
||||||
|
'SC70202602120085'
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('clears shared ids when parsed input is empty', async () => {
|
||||||
|
const clearSharedProductionIds = vi.fn(async () => undefined)
|
||||||
|
const setSharedProductionIds = vi.fn(async () => undefined)
|
||||||
|
|
||||||
|
await syncSharedProductionIdsWithApi(' \n\t', {
|
||||||
|
clearSharedProductionIds,
|
||||||
|
setSharedProductionIds
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(clearSharedProductionIds).toHaveBeenCalledTimes(1)
|
||||||
|
expect(setSharedProductionIds).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('writes parsed ids when input contains values', async () => {
|
||||||
|
const clearSharedProductionIds = vi.fn(async () => undefined)
|
||||||
|
const setSharedProductionIds = vi.fn(async () => undefined)
|
||||||
|
|
||||||
|
await syncSharedProductionIdsWithApi('22A1\nSC70202602120085', {
|
||||||
|
clearSharedProductionIds,
|
||||||
|
setSharedProductionIds
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(clearSharedProductionIds).not.toHaveBeenCalled()
|
||||||
|
expect(setSharedProductionIds).toHaveBeenCalledWith(['22A1', 'SC70202602120085'])
|
||||||
|
})
|
||||||
|
})
|
||||||
81
tests/unit/use-update-dialog-state.test.ts
Normal file
81
tests/unit/use-update-dialog-state.test.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import type { UpdateDialogCatalog, UpdateRelease } from '../../src/main/types/update.types'
|
||||||
|
import {
|
||||||
|
getInitialSelectedRelease,
|
||||||
|
releaseKey
|
||||||
|
} from '../../src/renderer/src/hooks/useUpdateDialogState'
|
||||||
|
|
||||||
|
const stableRelease: UpdateRelease = {
|
||||||
|
version: '1.4.1',
|
||||||
|
channel: 'stable',
|
||||||
|
artifactKey: 'stable-141.exe',
|
||||||
|
sha256: 'stable-hash',
|
||||||
|
size: 100,
|
||||||
|
publishedAt: '2026-03-21T10:00:00.000Z',
|
||||||
|
changelogKey: 'stable-141.md',
|
||||||
|
notesSummary: 'stable summary'
|
||||||
|
}
|
||||||
|
|
||||||
|
const previewRelease: UpdateRelease = {
|
||||||
|
version: '1.5.0-preview.1',
|
||||||
|
channel: 'preview',
|
||||||
|
artifactKey: 'preview-150.exe',
|
||||||
|
sha256: 'preview-hash',
|
||||||
|
size: 100,
|
||||||
|
publishedAt: '2026-03-22T10:00:00.000Z',
|
||||||
|
changelogKey: 'preview-150.md',
|
||||||
|
notesSummary: 'preview summary'
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useUpdateDialogState helpers', () => {
|
||||||
|
it('builds stable release keys', () => {
|
||||||
|
expect(releaseKey(stableRelease)).toBe('stable:1.4.1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined for disabled catalog', () => {
|
||||||
|
const catalog: UpdateDialogCatalog = { mode: 'disabled' }
|
||||||
|
expect(getInitialSelectedRelease(catalog)).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('prefers recommended user release', () => {
|
||||||
|
const catalog: UpdateDialogCatalog = {
|
||||||
|
mode: 'user',
|
||||||
|
recommendedRelease: stableRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(getInitialSelectedRelease(catalog)).toEqual(stableRelease)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('prefers recommended admin release before channel fallbacks', () => {
|
||||||
|
const catalog: UpdateDialogCatalog = {
|
||||||
|
mode: 'admin',
|
||||||
|
recommendedRelease: previewRelease,
|
||||||
|
channels: {
|
||||||
|
stable: [stableRelease],
|
||||||
|
preview: [previewRelease]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(getInitialSelectedRelease(catalog)).toEqual(previewRelease)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to stable then preview when admin recommendation is absent', () => {
|
||||||
|
const stableCatalog: UpdateDialogCatalog = {
|
||||||
|
mode: 'admin',
|
||||||
|
channels: {
|
||||||
|
stable: [stableRelease],
|
||||||
|
preview: [previewRelease]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const previewCatalog: UpdateDialogCatalog = {
|
||||||
|
mode: 'admin',
|
||||||
|
channels: {
|
||||||
|
stable: [],
|
||||||
|
preview: [previewRelease]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(getInitialSelectedRelease(stableCatalog)).toEqual(stableRelease)
|
||||||
|
expect(getInitialSelectedRelease(previewCatalog)).toEqual(previewRelease)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user