test(app): remove useless frontend tests (#2819)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-17 15:05:36 +02:00
1 parent ed9528834d
commit deac653a9e
341 files changed
-62758

No files matched your search

@@ -1,50 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
cssColorStringToHex,
cssColorStringToNumber,
numberToHex,
updateCssForVariable,
} from '@app/features/user/components/modals/tabs/appearance_tab/theme/ThemeUtils';
import {describe, expect, it} from 'vitest';
describe('updateCssForVariable', () => {
it('adds a token override when the CSS is empty', () => {
expect(updateCssForVariable('', '--background-primary', '#ff00ff')).toBe(
':root { --background-primary: #ff00ff; }\n',
);
});
it('updates an existing token override', () => {
expect(updateCssForVariable(':root { --background-primary: #ffffff; }\n', '--background-primary', '#111111')).toBe(
':root { --background-primary: #111111; }\n',
);
});
it('removes the empty root block after the last token override is cleared', () => {
expect(updateCssForVariable(':root { --background-primary: #ffffff; }\n', '--background-primary', null)).toBe('');
});
it('keeps unrelated CSS when clearing a token override', () => {
expect(
updateCssForVariable(
'body { color: red; }\n:root { --background-primary: #ffffff; }\n',
'--background-primary',
null,
),
).toBe('body { color: red; }');
});
});
describe('cssColorStringToNumber', () => {
it('parses black as a real color', () => {
expect(numberToHex(cssColorStringToNumber('#000000') ?? -1)).toBe('#000000');
expect(numberToHex(cssColorStringToNumber('rgb(0, 0, 0)') ?? -1)).toBe('#000000');
expect(numberToHex(cssColorStringToNumber('#000000 !important') ?? -1)).toBe('#000000');
});
it('does not turn invalid CSS values into black', () => {
expect(cssColorStringToNumber('1px solid hsl(0, 0%, 0%)')).toBeNull();
expect(cssColorStringToNumber('not-a-color')).toBeNull();
});
it('normalizes equivalent CSS colors to the same hex value', () => {
expect(cssColorStringToHex('rgb(0, 0, 0)')).toBe('#000000');
expect(cssColorStringToHex('#000000 !important')).toBe('#000000');
});
});
@@ -1,178 +0,0 @@
// @vitest-environment happy-dom
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {
EnsureVoiceDevicesOptions,
VoiceDeviceState,
VoiceMediaPermissionType,
} from '@app/features/voice/utils/VoiceDeviceManager';
import {act, createElement, useEffect} from 'react';
import {createRoot, type Root} from 'react-dom/client';
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
const deviceManagerMock = vi.hoisted(() => {
const idleState = (): VoiceDeviceState => ({
inputDevices: [],
outputDevices: [],
videoDevices: [],
permissionStatus: {audio: 'idle', video: 'idle'},
});
const listeners = new Set<(state: VoiceDeviceState) => void>();
const mock = {
state: idleState(),
permissionRequests: [] as Array<ReadonlyArray<VoiceMediaPermissionType>>,
pending: [] as Array<(state: VoiceDeviceState) => void>,
reset(): void {
mock.state = idleState();
mock.permissionRequests = [];
mock.pending = [];
listeners.clear();
},
settlePendingWith(next: VoiceDeviceState): void {
const pending = mock.pending;
mock.pending = [];
mock.state = next;
for (const listener of listeners) listener(next);
for (const resolve of pending) resolve(next);
},
manager: {
getState: (): VoiceDeviceState => mock.state,
subscribe: (listener: (state: VoiceDeviceState) => void): (() => void) => {
listeners.add(listener);
listener(mock.state);
return () => listeners.delete(listener);
},
ensureDevices: (options: EnsureVoiceDevicesOptions = {}): Promise<VoiceDeviceState> => {
const requestPermissionTypes =
options.requestPermissionTypes ?? (options.requestPermissions === true ? ['audio', 'video'] : []);
if (requestPermissionTypes.length === 0) return Promise.resolve(mock.state);
mock.permissionRequests.push([...requestPermissionTypes]);
return new Promise<VoiceDeviceState>((resolve) => {
mock.pending.push(resolve);
});
},
},
};
return mock;
});
const mediaPermissionMock = vi.hoisted(() => ({
microphoneExplicitlyDenied: false,
cameraExplicitlyDenied: false,
microphonePermissionState: null as PermissionState | null,
cameraPermissionState: null as PermissionState | null,
isMicrophoneGranted: vi.fn(() => false),
isCameraGranted: vi.fn(() => false),
updateMicrophonePermissionGranted: vi.fn(),
updateCameraPermissionGranted: vi.fn(),
markMicrophoneExplicitlyDenied: vi.fn(),
markCameraExplicitlyDenied: vi.fn(),
}));
const handleMediaPermissionBlocked = vi.hoisted(() => vi.fn());
vi.mock('@app/features/voice/utils/VoiceDeviceManager', () => ({voiceDeviceManager: deviceManagerMock.manager}));
vi.mock('@app/features/permissions/system/state/MediaPermission', () => ({default: mediaPermissionMock}));
vi.mock('@app/features/permissions/system/commands/MacPermissionsModalCommands', () => ({
handleMediaPermissionBlocked,
}));
const {useMediaPermission} = await import('@app/features/user/components/modals/tabs/hooks/useMediaPermission');
(globalThis as {IS_REACT_ACT_ENVIRONMENT?: boolean}).IS_REACT_ACT_ENVIRONMENT = true;
const MICROPHONE: MediaDeviceInfo = {
deviceId: 'mic-1',
groupId: 'group-1',
kind: 'audioinput',
label: 'Microphone',
toJSON: () => ({}),
};
const grantedState = (): VoiceDeviceState => ({
inputDevices: [MICROPHONE],
outputDevices: [],
videoDevices: [],
permissionStatus: {audio: 'granted', video: 'idle'},
});
const deniedState = (): VoiceDeviceState => ({
inputDevices: [],
outputDevices: [],
videoDevices: [],
permissionStatus: {audio: 'denied', video: 'idle'},
});
let host: HTMLDivElement;
let root: Root;
const requesters: Array<() => Promise<boolean>> = [];
function Consumer(): null {
const {requestPermission} = useMediaPermission('audio');
useEffect(() => {
requesters.push(requestPermission);
return () => {
const index = requesters.indexOf(requestPermission);
if (index !== -1) requesters.splice(index, 1);
};
}, [requestPermission]);
return null;
}
function renderConsumers(count: number): void {
act(() => {
root.render(Array.from({length: count}, (_, index) => createElement(Consumer, {key: index})));
});
}
beforeEach(() => {
deviceManagerMock.reset();
requesters.length = 0;
mediaPermissionMock.microphoneExplicitlyDenied = false;
mediaPermissionMock.microphonePermissionState = null;
mediaPermissionMock.isMicrophoneGranted.mockReturnValue(false);
host = document.createElement('div');
document.body.append(host);
root = createRoot(host);
});
afterEach(() => {
act(() => {
root.unmount();
});
document.body.replaceChildren();
vi.clearAllMocks();
});
describe('useMediaPermission', () => {
it('raises a single device permission request when two consumers ask at once', async () => {
renderConsumers(2);
expect(requesters).toHaveLength(2);
const results = await act(async () => {
const pending = Promise.all([requesters[0](), requesters[1]()]);
await Promise.resolve();
deviceManagerMock.settlePendingWith(grantedState());
return pending;
});
expect(deviceManagerMock.permissionRequests).toEqual([['audio']]);
expect(results).toEqual([true, true]);
expect(mediaPermissionMock.updateMicrophonePermissionGranted).toHaveBeenCalledTimes(1);
});
it('reports a denied request through the shared permission state', async () => {
renderConsumers(1);
const granted = await act(async () => {
const pending = requesters[0]();
await Promise.resolve();
deviceManagerMock.settlePendingWith(deniedState());
return pending;
});
expect(granted).toBe(false);
expect(mediaPermissionMock.markMicrophoneExplicitlyDenied).toHaveBeenCalledTimes(1);
expect(handleMediaPermissionBlocked).toHaveBeenCalledWith('microphone');
});
});
@@ -1,189 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
assignProfileAssetUploadPatch,
createGlobalProfileAssetRemoteState,
createProfileAssetCustomizationSnapshot,
createProfileAssetRemoteStateFromFlags,
getProfileAssetUploadPatch,
type ProfileAssetCustomizationEvent,
type ProfileAssetCustomizationSnapshot,
type ProfileAssetMode,
type ProfileAssetRemoteState,
selectProfileAssetCustomizationState,
transitionProfileAssetCustomizationSnapshot,
} from '@app/features/user/components/modals/tabs/my_profile_tab/ProfileAssetCustomizationStateMachine';
import {describe, expect, it} from 'vitest';
const IDENTITY = 'user-1:guild-1';
const OTHER_IDENTITY = 'user-1:guild-2';
const DATA_URL = 'data:image/png;base64,AAA';
function remote(mode: ProfileAssetMode, hasCustomAsset: boolean, identityKey = IDENTITY): ProfileAssetRemoteState {
return {identityKey, mode, hasCustomAsset};
}
function transition(
snapshot: ProfileAssetCustomizationSnapshot,
event: ProfileAssetCustomizationEvent,
): ProfileAssetCustomizationSnapshot {
return transitionProfileAssetCustomizationSnapshot(snapshot, event);
}
function hydrate(remoteState: ProfileAssetRemoteState): ProfileAssetCustomizationSnapshot {
return transition(createProfileAssetCustomizationSnapshot(), {type: 'asset.remoteApplied', remoteState});
}
describe('ProfileAssetCustomizationStateMachine', () => {
it('derives per-community asset modes from custom hashes and unset flags', () => {
expect(
createProfileAssetRemoteStateFromFlags({
identityKey: IDENTITY,
hasCustomAsset: false,
isUnset: false,
}),
).toEqual(remote('inherit', false));
expect(
createProfileAssetRemoteStateFromFlags({
identityKey: IDENTITY,
hasCustomAsset: true,
isUnset: false,
}),
).toEqual(remote('custom', true));
expect(
createProfileAssetRemoteStateFromFlags({
identityKey: IDENTITY,
hasCustomAsset: true,
isUnset: true,
}),
).toEqual(remote('unset', false));
});
it('tracks an accepted upload as a dirty custom asset patch', () => {
let snapshot = hydrate(remote('inherit', false));
snapshot = transition(snapshot, {type: 'asset.uploaded', previewUrl: DATA_URL});
const state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({
state: 'dirty',
mode: 'custom',
initialMode: 'inherit',
hasCustomAsset: true,
previewUrl: DATA_URL,
hasCleared: false,
hasAsset: true,
isDirty: true,
});
expect(getProfileAssetUploadPatch(state)).toEqual({value: DATA_URL});
});
it('sends null when a custom asset is changed back to inherit or unset', () => {
let snapshot = hydrate(remote('custom', true));
snapshot = transition(snapshot, {type: 'asset.modeSelected', mode: 'inherit'});
let state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({state: 'dirty', mode: 'inherit', hasCleared: false});
expect(getProfileAssetUploadPatch(state)).toEqual({value: null});
snapshot = hydrate(remote('custom', true));
snapshot = transition(snapshot, {type: 'asset.modeSelected', mode: 'unset'});
state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({state: 'dirty', mode: 'unset', hasCleared: true});
expect(getProfileAssetUploadPatch(state)).toEqual({value: null});
});
it('does not revert an accepted custom upload when a stale same-identity remote snapshot says inherit', () => {
let snapshot = hydrate(remote('inherit', false));
snapshot = transition(snapshot, {type: 'asset.uploaded', previewUrl: DATA_URL});
snapshot = transition(snapshot, {type: 'asset.committed', remoteState: remote('custom', true)});
let state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({
state: 'committed',
mode: 'custom',
initialMode: 'custom',
hasCustomAsset: true,
previewUrl: null,
isDirty: false,
});
snapshot = transition(snapshot, {type: 'asset.remoteApplied', remoteState: remote('inherit', false)});
state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({
state: 'committed',
mode: 'custom',
hasCustomAsset: true,
isDirty: false,
});
expect(getProfileAssetUploadPatch(state)).toEqual({value: undefined});
snapshot = transition(snapshot, {type: 'asset.remoteApplied', remoteState: remote('custom', true)});
state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({state: 'clean', mode: 'custom', hasCustomAsset: true});
});
it('protects dirty local edits from same-identity remote refreshes but accepts identity changes', () => {
let snapshot = hydrate(remote('inherit', false));
snapshot = transition(snapshot, {type: 'asset.uploaded', previewUrl: DATA_URL});
snapshot = transition(snapshot, {type: 'asset.remoteApplied', remoteState: remote('inherit', false)});
let state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({state: 'dirty', mode: 'custom', previewUrl: DATA_URL});
snapshot = transition(snapshot, {
type: 'asset.remoteApplied',
remoteState: remote('inherit', false, OTHER_IDENTITY),
});
state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({
state: 'clean',
identityKey: OTHER_IDENTITY,
mode: 'inherit',
previewUrl: null,
isDirty: false,
});
});
it('allows explicit same-identity resets to discard dirty local edits', () => {
let snapshot = hydrate(remote('inherit', false));
snapshot = transition(snapshot, {type: 'asset.uploaded', previewUrl: DATA_URL});
snapshot = transition(snapshot, {
type: 'asset.remoteApplied',
remoteState: remote('inherit', false),
force: true,
});
const state = selectProfileAssetCustomizationState(snapshot);
expect(state).toMatchObject({
state: 'clean',
mode: 'inherit',
previewUrl: null,
hasCleared: false,
isDirty: false,
});
});
it('assigns upload patches without writing undefined fields', () => {
const cleanState = selectProfileAssetCustomizationState(hydrate(remote('inherit', false)));
const dirtyState = selectProfileAssetCustomizationState(
transition(hydrate(remote('inherit', false)), {type: 'asset.uploaded', previewUrl: DATA_URL}),
);
const payload: {avatar?: string | null} = {};
assignProfileAssetUploadPatch(payload, 'avatar', cleanState);
expect(payload).toEqual({});
assignProfileAssetUploadPatch(payload, 'avatar', dirtyState);
expect(payload).toEqual({avatar: DATA_URL});
});
it('models global profile assets as custom or unset instead of inherit', () => {
expect(createGlobalProfileAssetRemoteState({identityKey: 'user-1:global', hasCustomAsset: true})).toEqual({
identityKey: 'user-1:global',
mode: 'custom',
hasCustomAsset: true,
});
expect(createGlobalProfileAssetRemoteState({identityKey: 'user-1:global', hasCustomAsset: false})).toEqual({
identityKey: 'user-1:global',
mode: 'unset',
hasCustomAsset: false,
});
});
});
@@ -1,30 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {isSettingsItemNew} from '@app/features/user/components/settings_utils/SettingsMetadata';
import {describe, expect, it} from 'vitest';
const NOW = new Date('2026-06-04T12:00:00.000Z');
describe('SettingsMetadata new badges', () => {
it('does not mark settings without an addedAt timestamp as new', () => {
expect(isSettingsItemNew({}, NOW)).toBe(false);
});
it('marks settings added within the freshness window as new', () => {
expect(isSettingsItemNew({addedAt: '2026-06-03T00:00:00.000Z'}, NOW)).toBe(true);
});
it('stops marking settings as new after 30 days', () => {
expect(isSettingsItemNew({addedAt: '2026-05-01T00:00:00.000Z'}, NOW)).toBe(false);
});
it('does not mark settings as new for accounts created after the setting was added', () => {
expect(isSettingsItemNew({addedAt: '2026-06-03T00:00:00.000Z'}, NOW, new Date('2026-06-04T00:00:00.000Z'))).toBe(
false,
);
});
it('does not mark future timestamps as new', () => {
expect(isSettingsItemNew({addedAt: '2026-06-05T00:00:00.000Z'}, NOW)).toBe(false);
});
});
@@ -1,102 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
PROFILE_MODAL_BANNER_AVATAR_CUTOUT,
PROFILE_MODAL_GEOMETRY,
PROFILE_MODAL_GEOMETRY_STYLE,
PROFILE_POPOUT_BANNER_AVATAR_CUTOUT,
PROFILE_POPOUT_GEOMETRY,
PROFILE_POPOUT_GEOMETRY_STYLE,
PROFILE_POPOUT_OUTER_HEIGHT_PX,
PROFILE_POPOUT_OUTER_WIDTH_PX,
} from '@app/features/user/constants/UserProfileSurfaceGeometry';
import {describe, expect, it} from 'vitest';
function rem(value: string): number {
const match = /^(-?\d+(?:\.\d+)?)rem$/.exec(value);
if (!match) {
throw new Error(`Expected rem length, got ${value}`);
}
return Number(match[1]) * 16;
}
describe('PROFILE_POPOUT_GEOMETRY', () => {
it('derives the reserved outer width from content width plus both borders', () => {
expect(PROFILE_POPOUT_OUTER_WIDTH_PX).toBe(
PROFILE_POPOUT_GEOMETRY.contentWidthPx + PROFILE_POPOUT_GEOMETRY.borderWidthPx * 2,
);
});
it('derives the reserved outer height from content height plus both borders', () => {
expect(PROFILE_POPOUT_OUTER_HEIGHT_PX).toBe(
PROFILE_POPOUT_GEOMETRY.contentHeightPx + PROFILE_POPOUT_GEOMETRY.borderWidthPx * 2,
);
});
it('exports rem CSS variables that exactly represent the geometry constants', () => {
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-content-width'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.contentWidthPx,
);
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-content-height'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.contentHeightPx,
);
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-border-width'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.borderWidthPx,
);
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-avatar-size'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.avatarSizePx,
);
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-avatar-border'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.avatarBorderPx,
);
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-banner-height'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.bannerHeightPx,
);
expect(rem(PROFILE_POPOUT_GEOMETRY_STYLE['--profile-popout-header-height'])).toBeCloseTo(
PROFILE_POPOUT_GEOMETRY.headerHeightPx,
);
});
it('emits only rem lengths for both profile geometry surfaces', () => {
for (const value of Object.values(PROFILE_POPOUT_GEOMETRY_STYLE)) {
expect(value).toMatch(/^-?\d+(?:\.\d+)?rem$/);
}
for (const value of Object.values(PROFILE_MODAL_GEOMETRY_STYLE)) {
expect(value).toMatch(/^-?\d+(?:\.\d+)?rem$/);
}
});
it('represents the modal geometry constants as rem CSS variables', () => {
expect(rem(PROFILE_MODAL_GEOMETRY_STYLE['--profile-modal-avatar-size'])).toBeCloseTo(
PROFILE_MODAL_GEOMETRY.avatarSizePx,
);
expect(rem(PROFILE_MODAL_GEOMETRY_STYLE['--profile-modal-avatar-border'])).toBeCloseTo(
PROFILE_MODAL_GEOMETRY.avatarBorderPx,
);
expect(rem(PROFILE_MODAL_GEOMETRY_STYLE['--profile-modal-avatar-left'])).toBeCloseTo(
PROFILE_MODAL_GEOMETRY.avatarLeftPx,
);
expect(rem(PROFILE_MODAL_GEOMETRY_STYLE['--profile-modal-avatar-top'])).toBeCloseTo(
PROFILE_MODAL_GEOMETRY.avatarTopPx,
);
});
it('proves the skeleton loading shell reserves the declared content rectangle plus both borders', () => {
const skeletonOuterWidth = PROFILE_POPOUT_GEOMETRY.contentWidthPx + PROFILE_POPOUT_GEOMETRY.borderWidthPx * 2;
const skeletonOuterHeight = PROFILE_POPOUT_GEOMETRY.contentHeightPx + PROFILE_POPOUT_GEOMETRY.borderWidthPx * 2;
expect(skeletonOuterWidth).toBe(PROFILE_POPOUT_OUTER_WIDTH_PX);
expect(skeletonOuterHeight).toBe(PROFILE_POPOUT_OUTER_HEIGHT_PX);
});
it('uses avatar status gutter widths for profile banner avatar cutouts', () => {
expect(PROFILE_POPOUT_GEOMETRY.avatarBorderPx).toBe(3.2);
expect(PROFILE_MODAL_GEOMETRY.avatarBorderPx).toBe(4.8);
expect(PROFILE_POPOUT_BANNER_AVATAR_CUTOUT.r).toBe(
PROFILE_POPOUT_GEOMETRY.avatarSizePx / 2 + PROFILE_POPOUT_GEOMETRY.avatarBorderPx,
);
expect(PROFILE_MODAL_BANNER_AVATAR_CUTOUT.r).toBe(
PROFILE_MODAL_GEOMETRY.avatarSizePx / 2 + PROFILE_MODAL_GEOMETRY.avatarBorderPx,
);
});
});
@@ -1,29 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import AppStorage from '@app/features/platform/state/PersistentStorage';
import {describe, expect, it} from 'vitest';
const KEEP_ATTACHMENTS_ON_EMPTY_MESSAGE_EDIT_STORAGE_KEY = 'AdvancedSettings:keepAttachmentsOnEmptyMessageEdit';
AppStorage.setItem(KEEP_ATTACHMENTS_ON_EMPTY_MESSAGE_EDIT_STORAGE_KEY, JSON.stringify(true));
const {default: AdvancedSettings} = await import('@app/features/user/state/AdvancedSettings');
describe('Advanced settings', () => {
it('rehydrates the stored attachment edit preference', () => {
expect(AdvancedSettings.keepAttachmentsOnEmptyMessageEdit).toBe(true);
});
it('stores the attachment edit preference under its own key', () => {
AdvancedSettings.setKeepAttachmentsOnEmptyMessageEdit(false);
expect(AppStorage.getItem(KEEP_ATTACHMENTS_ON_EMPTY_MESSAGE_EDIT_STORAGE_KEY)).toBe('false');
expect(AdvancedSettings.unreadBadgeCustomizationEnabled).toBe(false);
expect(AppStorage.getItem('AdvancedSettings:unreadBadgeCustomizationEnabled')).toBeNull();
});
it('defaults the expression clone shortcuts preference to off and stores it under its own key', () => {
expect(AdvancedSettings.expressionCloneShortcutsEnabled).toBe(false);
AdvancedSettings.setExpressionCloneShortcutsEnabled(true);
expect(AppStorage.getItem('AdvancedSettings:expressionCloneShortcutsEnabled')).toBe('true');
});
});
@@ -1,45 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {decideOversizePush} from '@app/features/user/state/SyncedFieldBudget';
import {describe, expect, it} from 'vitest';
const MAX = 1000;
describe('decideOversizePush', () => {
it('accepts a payload inside the budget', () => {
expect(decideOversizePush({encodedBytes: 999, maxEncodedBytes: MAX, lastPreparedBytes: null})).toBe(
'within-budget',
);
expect(decideOversizePush({encodedBytes: MAX, maxEncodedBytes: MAX, lastPreparedBytes: null})).toBe(
'within-budget',
);
});
it('drops the first over-budget payload', () => {
expect(decideOversizePush({encodedBytes: 1001, maxEncodedBytes: MAX, lastPreparedBytes: null})).toBe('drop');
});
it('drops an over-budget payload that grows', () => {
expect(decideOversizePush({encodedBytes: 1200, maxEncodedBytes: MAX, lastPreparedBytes: 1100})).toBe('drop');
});
it('drops an over-budget payload that stays the same size', () => {
expect(decideOversizePush({encodedBytes: 1100, maxEncodedBytes: MAX, lastPreparedBytes: 1100})).toBe('drop');
});
it('pushes an over-budget payload that shrinks so removals persist', () => {
expect(decideOversizePush({encodedBytes: 1050, maxEncodedBytes: MAX, lastPreparedBytes: 1100})).toBe(
'push-shrinks',
);
});
it('lets a run of removals drain back under the budget', () => {
let last: number | null = null;
const decisions: Array<string> = [];
for (const bytes of [1300, 1200, 1100, 900]) {
decisions.push(decideOversizePush({encodedBytes: bytes, maxEncodedBytes: MAX, lastPreparedBytes: last}));
last = bytes;
}
expect(decisions).toEqual(['drop', 'push-shrinks', 'push-shrinks', 'within-budget']);
});
});
@@ -1,177 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
createSyncedFieldMachineSnapshot,
type SyncedFieldMachineEvent,
type SyncedFieldMachineSnapshot,
selectSyncedFieldMachineModel,
transitionSyncedFieldMachineSnapshot,
} from '@app/features/user/state/SyncedFieldStateMachine';
import {describe, expect, it} from 'vitest';
const REMOTE_SNAPSHOT = Object.freeze({source: 'remote'});
const LOCAL_CANDIDATE = Object.freeze({source: 'local'});
function transition(snapshot: SyncedFieldMachineSnapshot, event: SyncedFieldMachineEvent): SyncedFieldMachineSnapshot {
return transitionSyncedFieldMachineSnapshot(snapshot, event);
}
describe('SyncedFieldStateMachine', () => {
it('starts active and idle without commands', () => {
const model = selectSyncedFieldMachineModel(createSyncedFieldMachineSnapshot());
expect(model.state).toBe('idle');
expect(model.isActive).toBe(true);
expect(model.command).toBeNull();
expect(model.suppressPush).toBe(false);
expect(model.failure).toBeNull();
});
it('skips missing or disabled remote snapshots', () => {
let snapshot = createSyncedFieldMachineSnapshot();
snapshot = transition(snapshot, {
type: 'sync.remoteObserved',
enabled: true,
snapshot: undefined,
});
expect(selectSyncedFieldMachineModel(snapshot).command).toBeNull();
snapshot = transition(snapshot, {
type: 'sync.remoteObserved',
enabled: false,
snapshot: REMOTE_SNAPSHOT,
});
const model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('idle');
expect(model.command).toBeNull();
});
it('compares a remote snapshot before deciding whether to apply it', () => {
let snapshot = createSyncedFieldMachineSnapshot();
snapshot = transition(snapshot, {
type: 'sync.remoteObserved',
enabled: true,
snapshot: REMOTE_SNAPSHOT,
});
let model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('comparingRemote');
expect(model.command).toEqual({type: 'compareRemote', snapshot: REMOTE_SNAPSHOT});
snapshot = transition(snapshot, {type: 'sync.remoteMatched'});
model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('idle');
expect(model.command).toBeNull();
});
it('suppresses local pushes while a remote snapshot is being applied', () => {
let snapshot = createSyncedFieldMachineSnapshot();
snapshot = transition(snapshot, {
type: 'sync.remoteObserved',
enabled: true,
snapshot: REMOTE_SNAPSHOT,
});
snapshot = transition(snapshot, {type: 'sync.remoteNeedsApply'});
let model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('applyingRemote');
expect(model.suppressPush).toBe(true);
expect(model.command).toEqual({type: 'applyRemote', snapshot: REMOTE_SNAPSHOT});
snapshot = transition(snapshot, {
type: 'sync.localObserved',
enabled: true,
candidate: LOCAL_CANDIDATE,
});
model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('applyingRemote');
expect(model.command).toEqual({type: 'applyRemote', snapshot: REMOTE_SNAPSHOT});
snapshot = transition(snapshot, {type: 'sync.applyFinished'});
model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('idle');
expect(model.suppressPush).toBe(false);
expect(model.command).toBeNull();
});
it('prepares and pushes a changed local candidate', () => {
let snapshot = createSyncedFieldMachineSnapshot();
snapshot = transition(snapshot, {
type: 'sync.localObserved',
enabled: true,
candidate: LOCAL_CANDIDATE,
});
let model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('preparingPush');
expect(model.command).toEqual({type: 'preparePush', candidate: LOCAL_CANDIDATE});
snapshot = transition(snapshot, {type: 'sync.localReadyToPush'});
model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('pushing');
expect(model.command).toEqual({type: 'pushLocal', candidate: LOCAL_CANDIDATE});
snapshot = transition(snapshot, {type: 'sync.commandHandled'});
model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('idle');
expect(model.command).toBeNull();
});
it('skips local candidates that are disabled, absent, or already synced', () => {
let snapshot = createSyncedFieldMachineSnapshot();
snapshot = transition(snapshot, {
type: 'sync.localObserved',
enabled: false,
candidate: LOCAL_CANDIDATE,
});
expect(selectSyncedFieldMachineModel(snapshot).command).toBeNull();
snapshot = transition(snapshot, {
type: 'sync.localObserved',
enabled: true,
candidate: null,
});
expect(selectSyncedFieldMachineModel(snapshot).command).toBeNull();
snapshot = transition(snapshot, {
type: 'sync.localObserved',
enabled: true,
candidate: LOCAL_CANDIDATE,
});
snapshot = transition(snapshot, {type: 'sync.localAlreadySynced'});
const model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('idle');
expect(model.command).toBeNull();
});
it('suspends on terminal failures and ignores further sync events until reset', () => {
let snapshot = createSyncedFieldMachineSnapshot();
snapshot = transition(snapshot, {
type: 'sync.failed',
failure: {
reason: 'payload-too-large',
message: 'too large',
},
});
let model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('suspended');
expect(model.isActive).toBe(false);
expect(model.failure?.reason).toBe('payload-too-large');
expect(model.command).toBeNull();
snapshot = transition(snapshot, {
type: 'sync.localObserved',
enabled: true,
candidate: LOCAL_CANDIDATE,
});
expect(selectSyncedFieldMachineModel(snapshot).state).toBe('suspended');
expect(selectSyncedFieldMachineModel(snapshot).command).toBeNull();
snapshot = transition(snapshot, {type: 'sync.reset'});
model = selectSyncedFieldMachineModel(snapshot);
expect(model.state).toBe('idle');
expect(model.isActive).toBe(true);
expect(model.failure).toBeNull();
});
});
@@ -1,65 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
resolveUnreadInboxVisibility,
type UnreadInboxVisibilityInput,
} from '@app/features/user/state/UnreadInboxVisibility';
import {MessageNotifications} from '@fluxer/constants/src/NotificationConstants';
import {describe, expect, it} from 'vitest';
function input(overrides: Partial<UnreadInboxVisibilityInput> = {}): UnreadInboxVisibilityInput {
return {
unreadBadgesLevel: null,
isMuted: false,
hasUnread: true,
hasMentions: false,
...overrides,
};
}
describe('resolveUnreadInboxVisibility', () => {
it('shows an unmuted channel with plain unread messages', () => {
expect(resolveUnreadInboxVisibility(input())).toBe(true);
});
it('shows a channel in a large community, where notification level is forced to only-mentions', () => {
expect(resolveUnreadInboxVisibility(input({unreadBadgesLevel: null, hasMentions: false}))).toBe(true);
});
it('hides a channel with nothing unread and no mentions', () => {
expect(resolveUnreadInboxVisibility(input({hasUnread: false, hasMentions: false}))).toBe(false);
});
it('hides plain unread in a muted channel', () => {
expect(resolveUnreadInboxVisibility(input({isMuted: true}))).toBe(false);
});
it('shows a muted channel when it has a mention', () => {
expect(resolveUnreadInboxVisibility(input({isMuted: true, hasMentions: true}))).toBe(true);
});
it('hides a channel whose explicit unread badges level is nothing, even with a mention', () => {
expect(
resolveUnreadInboxVisibility(input({unreadBadgesLevel: MessageNotifications.NO_MESSAGES, hasMentions: true})),
).toBe(false);
});
it('hides plain unread when the explicit unread badges level is only-mentions', () => {
expect(resolveUnreadInboxVisibility(input({unreadBadgesLevel: MessageNotifications.ONLY_MENTIONS}))).toBe(false);
});
it('shows a mention when the explicit unread badges level is only-mentions', () => {
expect(
resolveUnreadInboxVisibility(input({unreadBadgesLevel: MessageNotifications.ONLY_MENTIONS, hasMentions: true})),
).toBe(true);
});
it('shows plain unread when the explicit unread badges level is all messages', () => {
expect(resolveUnreadInboxVisibility(input({unreadBadgesLevel: MessageNotifications.ALL_MESSAGES}))).toBe(true);
});
it('takes no notification-level input at all, so a forced community level cannot gate it', () => {
const keys = Object.keys(input()).sort();
expect(keys).toEqual(['hasMentions', 'hasUnread', 'isMuted', 'unreadBadgesLevel']);
});
});
@@ -1,66 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {Profile} from '@app/features/user/models/Profile';
import {afterEach, describe, expect, it, vi} from 'vitest';
vi.mock('@app/features/auth/state/Authentication', () => ({default: {currentUserId: null}}));
const {default: UserProfile} = await import('@app/features/user/state/UserProfile');
const GUILD_ID = '1000';
const TARGET_ID = '2000';
function cache(userId: string = TARGET_ID, guildId?: string): void {
UserProfile.handleProfileCreate({
userId,
guildId: guildId ?? null,
mutualGuilds: [{id: GUILD_ID, nick: null}],
} as unknown as Profile);
}
describe('UserProfile cache invalidation', () => {
afterEach(() => {
UserProfile.handleGatewayReady();
});
it('drops every cached scope for a member removed from a community', () => {
cache();
cache(TARGET_ID, GUILD_ID);
expect(UserProfile.getProfile(TARGET_ID)?.mutualGuilds).toHaveLength(1);
UserProfile.handleGuildMemberRemove(TARGET_ID);
expect(UserProfile.getProfile(TARGET_ID)).toBeNull();
expect(UserProfile.getProfile(TARGET_ID, GUILD_ID)).toBeNull();
});
it('drops every cached scope for a member who joined a community', () => {
cache();
UserProfile.handleGuildMemberAdd(TARGET_ID);
expect(UserProfile.getProfile(TARGET_ID)).toBeNull();
});
it('keeps other users cached when one member is removed', () => {
cache();
cache('3000');
UserProfile.handleGuildMemberRemove(TARGET_ID);
expect(UserProfile.getProfile(TARGET_ID)).toBeNull();
expect(UserProfile.getProfile('3000')).not.toBeNull();
});
it('drops cached profiles when the viewer leaves a community', () => {
cache();
UserProfile.handleGuildDelete(false);
expect(UserProfile.getProfile(TARGET_ID)).toBeNull();
});
it('keeps cached profiles when a community only goes unavailable', () => {
cache();
UserProfile.handleGuildDelete(true);
expect(UserProfile.getProfile(TARGET_ID)).not.toBeNull();
});
it('drops cached profiles when the viewer joins a community', () => {
cache();
UserProfile.handleGuildCreate();
expect(UserProfile.getProfile(TARGET_ID)).toBeNull();
});
});
@@ -1,46 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {isVisionaryDiscriminator0000Blocked} from '@app/features/user/utils/FluxerTagDiscriminatorUtils';
import {describe, expect, it} from 'vitest';
describe('isVisionaryDiscriminator0000Blocked', () => {
it('allows #0000 on self-hosted instances', () => {
expect(
isVisionaryDiscriminator0000Blocked({
showPremium: false,
isVisionary: false,
discriminator: '0000',
}),
).toBe(false);
});
it('blocks #0000 for non-visionary users on the main instance', () => {
expect(
isVisionaryDiscriminator0000Blocked({
showPremium: true,
isVisionary: false,
discriminator: '0000',
}),
).toBe(true);
});
it('allows #0000 for visionary users on the main instance', () => {
expect(
isVisionaryDiscriminator0000Blocked({
showPremium: true,
isVisionary: true,
discriminator: '0000',
}),
).toBe(false);
});
it('allows other discriminators on the main instance', () => {
expect(
isVisionaryDiscriminator0000Blocked({
showPremium: true,
isVisionary: false,
discriminator: '1337',
}),
).toBe(false);
});
});