9f4516c6a8
Via subagent, all verified against real behavior: - state/sessions (5): fallback-session round-trip across the four cinny_* keys, missing-key → undefined for each required key, removeFallbackSession clears all. - state/recentSearches (6): addRecentSearch prepend, case-sensitive dedupe + move-to-front, trim, ignore empty/whitespace, cap at 10. - state/upload (6): the createUploadAtom reducer driven through a real jotai store — idle→loading→progress(gated)→success/error, file ref preserved. No bugs found. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createStore } from 'jotai';
|
|
import { UploadResponse, UploadProgress, MatrixError } from 'matrix-js-sdk';
|
|
import { createUploadAtom, UploadStatus, Upload } from './upload';
|
|
import { TUploadContent } from '../utils/matrix';
|
|
|
|
// We exercise the pure reducer inside `createUploadAtom` by driving it through a
|
|
// plain jotai store. The write atom is a state machine over UploadStatus.
|
|
//
|
|
// SKIPPED (require a real MatrixClient and/or React render, not pure logic):
|
|
// - useBindUploadAtom (React hook; calls mx.cancelUpload, uploadContent)
|
|
// - createUploadAtomFamily / createUploadFamilyObserverAtom (thin atomFamily
|
|
// wrappers whose behavior is just createUploadAtom + jotai plumbing)
|
|
|
|
const makeFile = (size = 100): TUploadContent => ({ size }) as unknown as TUploadContent;
|
|
|
|
test('createUploadAtom starts in the Idle state holding the file', () => {
|
|
const store = createStore();
|
|
const file = makeFile();
|
|
const uploadAtom = createUploadAtom(file);
|
|
|
|
const state = store.get(uploadAtom);
|
|
assert.equal(state.status, UploadStatus.Idle);
|
|
assert.equal(state.file, file);
|
|
});
|
|
|
|
test('a promise update transitions Idle -> Loading with zeroed progress', () => {
|
|
const store = createStore();
|
|
const file = makeFile(2048);
|
|
const uploadAtom = createUploadAtom(file);
|
|
|
|
const promise = Promise.resolve({} as UploadResponse);
|
|
store.set(uploadAtom, { promise });
|
|
|
|
const state = store.get(uploadAtom);
|
|
assert.equal(state.status, UploadStatus.Loading);
|
|
if (state.status === UploadStatus.Loading) {
|
|
assert.equal(state.promise, promise);
|
|
assert.deepEqual(state.progress, { loaded: 0, total: 2048 });
|
|
}
|
|
});
|
|
|
|
test('a progress update is applied only while Loading', () => {
|
|
const store = createStore();
|
|
const uploadAtom = createUploadAtom(makeFile(2048));
|
|
|
|
const progress: UploadProgress = { loaded: 512, total: 2048 };
|
|
|
|
// Ignored while Idle (not Loading).
|
|
store.set(uploadAtom, { progress });
|
|
assert.equal(store.get(uploadAtom).status, UploadStatus.Idle);
|
|
|
|
// Enter Loading, then progress sticks.
|
|
store.set(uploadAtom, { promise: Promise.resolve({} as UploadResponse) });
|
|
store.set(uploadAtom, { progress });
|
|
const state = store.get(uploadAtom);
|
|
assert.equal(state.status, UploadStatus.Loading);
|
|
if (state.status === UploadStatus.Loading) {
|
|
assert.deepEqual(state.progress, progress);
|
|
}
|
|
});
|
|
|
|
test('an mxc update transitions to Success', () => {
|
|
const store = createStore();
|
|
const uploadAtom = createUploadAtom(makeFile());
|
|
|
|
store.set(uploadAtom, { mxc: 'mxc://example.org/abc' });
|
|
const state = store.get(uploadAtom);
|
|
assert.equal(state.status, UploadStatus.Success);
|
|
if (state.status === UploadStatus.Success) {
|
|
assert.equal(state.mxc, 'mxc://example.org/abc');
|
|
}
|
|
});
|
|
|
|
test('an error update transitions to Error', () => {
|
|
const store = createStore();
|
|
const uploadAtom = createUploadAtom(makeFile());
|
|
|
|
const error = new Error('boom') as unknown as MatrixError;
|
|
store.set(uploadAtom, { error });
|
|
const state = store.get(uploadAtom);
|
|
assert.equal(state.status, UploadStatus.Error);
|
|
if (state.status === UploadStatus.Error) {
|
|
assert.equal(state.error, error);
|
|
}
|
|
});
|
|
|
|
test('the file reference is preserved across transitions', () => {
|
|
const store = createStore();
|
|
const file = makeFile();
|
|
const uploadAtom = createUploadAtom(file);
|
|
|
|
const seenFiles: TUploadContent[] = [];
|
|
const record = (s: Upload) => seenFiles.push(s.file);
|
|
|
|
store.set(uploadAtom, { promise: Promise.resolve({} as UploadResponse) });
|
|
record(store.get(uploadAtom));
|
|
store.set(uploadAtom, { mxc: 'mxc://example.org/x' });
|
|
record(store.get(uploadAtom));
|
|
|
|
assert.ok(seenFiles.every((f) => f === file));
|
|
});
|