Files
2026-07-13 13:20:22 +08:00

191 lines
6.2 KiB
TypeScript

/**
* @license
* Copyright 2025 AionUi (aionui.com)
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { uuid, parseError, resolveLocaleKey } from '@/common/utils/utils';
describe('utils', () => {
describe('uuid', () => {
it('generates string of default length 8', () => {
const id = uuid();
expect(typeof id).toBe('string');
expect(id.length).toBe(8);
});
it('generates string of custom length', () => {
expect(uuid(16).length).toBe(16);
expect(uuid(32).length).toBe(32);
});
it('generates different ids on successive calls', () => {
const id1 = uuid();
const id2 = uuid();
expect(id1).not.toBe(id2);
});
it('uses crypto.randomUUID when length >= 36', () => {
const id = uuid(36);
expect(id.length).toBe(36);
// UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/);
});
it('uses crypto.getRandomValues for lengths < 36', () => {
const id = uuid(12);
expect(id.length).toBe(12);
expect(id).toMatch(/^[0-9a-f]{12}$/);
});
it('falls back to timestamp-based ID when crypto unavailable', () => {
const originalCrypto = globalThis.crypto;
Object.defineProperty(globalThis, 'crypto', {
value: undefined,
writable: true,
configurable: true,
});
const id = uuid(10);
expect(id.length).toBe(10);
Object.defineProperty(globalThis, 'crypto', {
value: originalCrypto,
writable: true,
configurable: true,
});
});
});
describe('parseError', () => {
it('returns string as-is', () => {
expect(parseError('error message')).toBe('error message');
});
it('extracts message from Error instance', () => {
const error = new Error('test error');
expect(parseError(error)).toBe('test error');
});
it('extracts msg property from object', () => {
const error = { msg: 'custom error' };
expect(parseError(error)).toBe('custom error');
});
it('extracts message property from object', () => {
const error = { message: 'object error' };
expect(parseError(error)).toBe('object error');
});
it('prefers msg over message', () => {
const error = { msg: 'msg value', message: 'message value' };
expect(parseError(error)).toBe('msg value');
});
it('prefers backendMessage over wrapped error message', () => {
const error = Object.assign(new Error('wrapped error'), {
backendMessage: 'raw backend error',
});
expect(parseError(error)).toBe('raw backend error');
});
it('stringifies object without msg/message', () => {
const error = { code: 500, status: 'fail' };
expect(parseError(error)).toBe('{"code":500,"status":"fail"}');
});
it('handles null', () => {
expect(parseError(null)).toBe('null');
});
it('handles undefined', () => {
// JSON.stringify(undefined) returns undefined, not a string
expect(parseError(undefined)).toBeUndefined();
});
it('handles number', () => {
expect(parseError(404)).toBe('404');
});
it('handles boolean', () => {
expect(parseError(true)).toBe('true');
});
it('handles circular reference gracefully', () => {
const error: { name: string; self?: unknown } = { name: 'circular' };
error.self = error;
expect(typeof parseError(error)).toBe('string');
});
});
describe('resolveLocaleKey', () => {
it('resolves zh variants to zh-CN', () => {
expect(resolveLocaleKey('zh')).toBe('zh-CN');
expect(resolveLocaleKey('zh-CN')).toBe('zh-CN');
expect(resolveLocaleKey('zh-Hans')).toBe('zh-CN');
});
it('resolves zh-TW to zh-TW', () => {
expect(resolveLocaleKey('zh-TW')).toBe('zh-TW');
expect(resolveLocaleKey('zh-tw')).toBe('zh-TW');
expect(resolveLocaleKey('zh-Hant')).toBe('zh-CN'); // Falls back to CN
});
it('resolves ja variants to ja-JP', () => {
expect(resolveLocaleKey('ja')).toBe('ja-JP');
expect(resolveLocaleKey('ja-JP')).toBe('ja-JP');
});
it('resolves ko variants to ko-KR', () => {
expect(resolveLocaleKey('ko')).toBe('ko-KR');
expect(resolveLocaleKey('ko-KR')).toBe('ko-KR');
});
it('resolves tr variants to tr-TR', () => {
expect(resolveLocaleKey('tr')).toBe('tr-TR');
expect(resolveLocaleKey('tr-TR')).toBe('tr-TR');
});
it('resolves ru, uk, pt, de, es, fr, and fa variants to supported locales', () => {
expect(resolveLocaleKey('ru')).toBe('ru-RU');
expect(resolveLocaleKey('ru-RU')).toBe('ru-RU');
expect(resolveLocaleKey('uk')).toBe('uk-UA');
expect(resolveLocaleKey('uk-UA')).toBe('uk-UA');
expect(resolveLocaleKey('pt')).toBe('pt-BR');
expect(resolveLocaleKey('pt-BR')).toBe('pt-BR');
expect(resolveLocaleKey('de')).toBe('de-DE');
expect(resolveLocaleKey('de-DE')).toBe('de-DE');
expect(resolveLocaleKey('es')).toBe('es-ES');
expect(resolveLocaleKey('es-ES')).toBe('es-ES');
expect(resolveLocaleKey('fr')).toBe('fr-FR');
expect(resolveLocaleKey('fr-FR')).toBe('fr-FR');
expect(resolveLocaleKey('fa')).toBe('fa-IR');
expect(resolveLocaleKey('fa-IR')).toBe('fa-IR');
});
it('resolves unknown languages to en-US', () => {
expect(resolveLocaleKey('en')).toBe('en-US');
expect(resolveLocaleKey('en-US')).toBe('en-US');
expect(resolveLocaleKey('it')).toBe('en-US');
});
it('is case-insensitive', () => {
expect(resolveLocaleKey('ZH')).toBe('zh-CN');
expect(resolveLocaleKey('JA')).toBe('ja-JP');
expect(resolveLocaleKey('KO')).toBe('ko-KR');
expect(resolveLocaleKey('TR')).toBe('tr-TR');
expect(resolveLocaleKey('RU')).toBe('ru-RU');
expect(resolveLocaleKey('UK')).toBe('uk-UA');
expect(resolveLocaleKey('PT')).toBe('pt-BR');
expect(resolveLocaleKey('ES')).toBe('es-ES');
expect(resolveLocaleKey('FR')).toBe('fr-FR');
expect(resolveLocaleKey('FA')).toBe('fa-IR');
});
it('handles empty string', () => {
expect(resolveLocaleKey('')).toBe('en-US');
});
});
});