Files
readest/apps/readest-app/src/__tests__/services/transferMessages.test.ts
T
Huang Xin 981579c255 feat(sync): cross-device custom font sync (#4077)
* refactor(sync): kind-agnostic replica primitives

Extract dict-only sync into shared primitives (registry, pull/apply
orchestrator, persist env, schema allowlist) so other kinds can plug
in. Companion changes: per-replica Storage Manager grouping,
useReplicaPull boot-race recovery, manifest=null reconciliation on
every boot pull, copyFile takes explicit srcBase + dstBase, settled-
event helpers, lenient webDownload Content-Length (R2/S3 signed URLs
commonly omit it), and generic "File" transfer toast copy any replica
kind can share.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sync): cross-device custom font sync

Plug the font replica adapter into the kind-agnostic primitives:
font store gains replica wiring, custom font import publishes the
replica row + queues a binary upload, and bootstrap registers the
font adapter and download-complete handler.

Includes legacy flat-path migration so pre-existing fonts sync
without re-import, full @font-face activation on auto-download
(load + mount the rule, mirroring manual import), and a fix to
createCustomFont so contentId / bundleDir / byteSize survive the
trip through addFont — otherwise import-time publish silently
no-oped on missing contentId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:28:44 +02:00

70 lines
2.7 KiB
TypeScript

import { describe, expect, test } from 'vitest';
import { getTransferMessages } from '@/services/transferMessages';
import type { TransferItem } from '@/store/transferStore';
import type { TranslationFunc } from '@/hooks/useTranslation';
const passthroughT: TranslationFunc = (key, params) => {
if (!params) return key;
return Object.entries(params).reduce((acc, [k, v]) => acc.replace(`{{${k}}}`, String(v)), key);
};
const baseTransfer = (overrides: Partial<TransferItem> = {}): TransferItem =>
({
id: 't1',
kind: 'book',
bookHash: 'h1',
bookTitle: 'Moby Dick',
type: 'upload',
status: 'pending',
progress: 0,
totalBytes: 0,
transferredBytes: 0,
transferSpeed: 0,
retryCount: 0,
maxRetries: 3,
createdAt: 0,
priority: 10,
isBackground: false,
...overrides,
}) as TransferItem;
describe('getTransferMessages', () => {
test('book transfer uses "Book" copy', () => {
const m = getTransferMessages(baseTransfer({ bookTitle: 'Moby Dick' }), passthroughT);
expect(m.success.upload).toBe('Book uploaded: Moby Dick');
expect(m.success.download).toBe('Book downloaded: Moby Dick');
expect(m.success.delete).toBe('Deleted cloud backup of the book: Moby Dick');
expect(m.failure.upload).toBe('Failed to upload book: Moby Dick');
expect(m.failure.download).toBe('Failed to download book: Moby Dick');
expect(m.failure.delete).toBe('Failed to delete cloud backup of the book: Moby Dick');
});
test('dictionary replica transfer uses generic "File" copy', () => {
const m = getTransferMessages(
baseTransfer({
kind: 'replica',
replicaKind: 'dictionary',
bookHash: '',
bookTitle: 'Longman Phrasal Verbs',
}),
passthroughT,
);
expect(m.success.upload).toBe('File uploaded: Longman Phrasal Verbs');
expect(m.success.download).toBe('File downloaded: Longman Phrasal Verbs');
expect(m.success.delete).toBe('Deleted cloud copy of the file: Longman Phrasal Verbs');
expect(m.failure.upload).toBe('Failed to upload file: Longman Phrasal Verbs');
expect(m.failure.download).toBe('Failed to download file: Longman Phrasal Verbs');
expect(m.failure.delete).toBe('Failed to delete cloud copy of the file: Longman Phrasal Verbs');
});
test('font replica transfer also uses generic "File" copy', () => {
const m = getTransferMessages(
baseTransfer({ kind: 'replica', replicaKind: 'font', bookTitle: 'Roboto' }),
passthroughT,
);
expect(m.success.upload).toBe('File uploaded: Roboto');
expect(m.success.download).toBe('File downloaded: Roboto');
expect(m.success.delete).toBe('Deleted cloud copy of the file: Roboto');
});
});