/
githubmirror
/
axios
Обзор
Документация
Войти
/
githubmirror
/
axios
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.x
tests/unit/utils.test.js
291 строка
10 KB
JSap0914
fix(utils): serialize Set values in toJSONObject (#11044)
01 июл 2026, 21:47
Не верифицирован
01 июл 2026, 21:47
e435384
Код
Авторство
О чём код?
import { describe, it } from 'vitest'; import assert from 'assert'; import utils from '../../lib/utils.js'; import FormData from 'form-data'; import stream from 'stream'; import vm from 'node:vm'; describe('utils', () => { it('should validate Stream', () => { assert.strictEqual(utils.isStream(new stream.Readable()), true); assert.strictEqual(utils.isStream({ foo: 'bar' }), false); }); it('should validate Buffer', () => { assert.strictEqual(utils.isBuffer(Buffer.from('a')), true); assert.strictEqual(utils.isBuffer(null), false); assert.strictEqual(utils.isBuffer(undefined), false); }); describe('utils::isFormData', () => { it('should detect the FormData instance provided by the `form-data` package', () => { [1, 'str', {}, new RegExp()].forEach((thing) => { assert.equal(utils.isFormData(thing), false); }); assert.equal(utils.isFormData(new FormData()), true); }); it('should not call toString method on built-in objects instances', () => { const buf = Buffer.from('123'); buf.toString = () => assert.fail('should not be called'); assert.equal(utils.isFormData(buf), false); }); it('should not call toString method on built-in objects instances, even if append method exists', () => { const buf = Buffer.from('123'); buf.append = () => {}; buf.toString = () => assert.fail('should not be called'); assert.equal(utils.isFormData(buf), false); }); it('should detect custom FormData instances by toStringTag signature and append method presence', () => { class FormData { append() {} get [Symbol.toStringTag]() { return 'FormData'; } } assert.equal(utils.isFormData(new FormData()), true); }); }); describe('toJSON', () => { it('should convert to a plain object without circular references', () => { const obj = { a: [0] }; const source = { x: 1, y: 2, obj }; source.circular1 = source; obj.a[1] = obj; assert.deepStrictEqual(utils.toJSONObject(source), { x: 1, y: 2, obj: { a: [0] }, }); }); it('should use objects with defined toJSON method without rebuilding', () => { const objProp = {}; const obj = { objProp, toJSON() { return { ok: 1 }; }, }; const source = { x: 1, y: 2, obj }; const jsonObject = utils.toJSONObject(source); assert.strictEqual(jsonObject.obj.objProp, objProp); assert.strictEqual( JSON.stringify(jsonObject), JSON.stringify({ x: 1, y: 2, obj: { ok: 1 } }) ); }); describe('cycle / DAG handling', () => { it('should serialize a shared sibling object at every occurrence (DAG, not cycle)', () => { const shared = { val: 42 }; const source = { x: shared, y: shared }; const result = utils.toJSONObject(source); // Both branches must serialize — shared reference is not a cycle assert.deepStrictEqual(result, { x: { val: 42 }, y: { val: 42 } }); }); it('should serialize a shared sibling array at every occurrence (DAG, not cycle)', () => { const shared = [1, 2, 3]; const source = { a: shared, b: shared }; const result = utils.toJSONObject(source); assert.deepStrictEqual(result, { a: [1, 2, 3], b: [1, 2, 3] }); }); it('should serialize shared sibling that itself contains a self-cycle', () => { const shared = { v: 1 }; shared.self = shared; // self-cycle inside the shared node const source = { x: shared, y: shared }; const result = utils.toJSONObject(source); // The self-cycle is stripped, but both x and y must be serialized assert.deepStrictEqual(result, { x: { v: 1 }, y: { v: 1 } }); }); it('should serialize non-cyclic structures deeper than the old Array(10) cap', () => { // The previous implementation used a fixed-size Array(10) for path tracking. // A non-cyclic chain deeper than 10 levels must serialise end-to-end. let leaf = { v: 'leaf' }; let source = leaf; for (let i = 0; i < 25; i++) { source = { next: source }; } const result = utils.toJSONObject(source); let cursor = result; for (let i = 0; i < 25; i++) { cursor = cursor.next; } assert.deepStrictEqual(cursor, { v: 'leaf' }); }); }); // https://github.com/axios/axios/issues/5910 describe('Set handling', () => { it('should convert a flat Set to an array', () => { const result = utils.toJSONObject(new Set([1, 2, 3])); assert.deepStrictEqual(result, [1, 2, 3]); }); it('should convert a Set nested inside a plain object', () => { const result = utils.toJSONObject({ tags: new Set(['a', 'b', 'c']) }); assert.deepStrictEqual(result, { tags: ['a', 'b', 'c'] }); }); it('should convert a cross-realm Set to an array', () => { const set = vm.runInNewContext('new Set(["a", "b", "c"])'); assert.strictEqual(set instanceof Set, false); assert.deepStrictEqual(utils.toJSONObject({ tags: set }), { tags: ['a', 'b', 'c'] }); }); it('should convert nested Sets recursively', () => { const inner = new Set(['x', 'y']); const outer = new Set([inner, 1]); assert.deepStrictEqual(utils.toJSONObject(outer), [['x', 'y'], 1]); }); it('should skip undefined values inside a Set (mirrors object/array behaviour)', () => { const result = utils.toJSONObject(new Set([1, undefined, 3])); assert.deepStrictEqual(result, [1, 3]); }); it('should handle a Set that appears in multiple places (DAG, not cycle)', () => { const shared = new Set([42]); const source = { a: shared, b: shared }; assert.deepStrictEqual(utils.toJSONObject(source), { a: [42], b: [42] }); }); it('should not follow a Set that directly contains itself (self-cycle)', () => { const s = new Set(); s.add(s); // self-referential s.add(1); // The cyclic self-reference is skipped; other values are kept. const result = utils.toJSONObject(s); assert.deepStrictEqual(result, [1]); }); it('should reproduce the original issue #5910 test case', () => { const obj = { a: [0] }; const source = { x: 1, y: 2, obj }; source.circular1 = source; obj.a[1] = obj; obj.b = new Set(['a', 'b']); assert.deepStrictEqual(utils.toJSONObject(source), { x: 1, y: 2, obj: { a: [0], b: ['a', 'b'] }, }); }); }); }); describe('Buffer RangeError Fix', () => { it('should handle large Buffer in isEmptyObject without RangeError', () => { const largeBuffer = Buffer.alloc(1024 * 1024 * 200); const result = utils.isEmptyObject(largeBuffer); assert.strictEqual(result, false); }); it('should handle large Buffer in forEach without RangeError', () => { const largeBuffer = Buffer.alloc(1024 * 1024 * 200); let count = 0; utils.forEach(largeBuffer, () => count++); assert.strictEqual(count, 0); }); it('should handle large Buffer in findKey without RangeError', () => { const largeBuffer = Buffer.alloc(1024 * 1024 * 200); const result = utils.findKey(largeBuffer, 'test'); assert.strictEqual(result, null); }); }); describe('utils::isReactNativeBlob', () => { it('should return true for objects with uri property', () => { assert.strictEqual(utils.isReactNativeBlob({ uri: 'file://path/to/file' }), true); assert.strictEqual(utils.isReactNativeBlob({ uri: 'content://media/image' }), true); }); it('should return true for React Native blob-like objects with optional name and type', () => { assert.strictEqual( utils.isReactNativeBlob({ uri: 'file://path/to/file', name: 'image.png', type: 'image/png', }), true ); }); it('should return false for objects without uri property', () => { assert.strictEqual(utils.isReactNativeBlob({ path: 'file://path' }), false); assert.strictEqual(utils.isReactNativeBlob({ url: 'http://example.com' }), false); assert.strictEqual(utils.isReactNativeBlob({}), false); }); it('should return false for non-objects', () => { assert.strictEqual(utils.isReactNativeBlob(null), false); assert.strictEqual(utils.isReactNativeBlob(undefined), false); assert.strictEqual(utils.isReactNativeBlob('string'), false); assert.strictEqual(utils.isReactNativeBlob(123), false); assert.strictEqual(utils.isReactNativeBlob(false), false); }); it('should return true even if uri is empty string', () => { assert.strictEqual(utils.isReactNativeBlob({ uri: '' }), true); }); }); describe('utils::isReactNative', () => { it('should return true for FormData with getParts method', () => { const mockReactNativeFormData = { append: () => {}, getParts: () => { return []; }, }; assert.strictEqual(utils.isReactNative(mockReactNativeFormData), true); }); it('should return false for standard FormData without getParts method', () => { const standardFormData = new FormData(); assert.strictEqual(utils.isReactNative(standardFormData), false); }); it('should return false for objects without getParts method', () => { assert.strictEqual(utils.isReactNative({ append: () => {} }), false); assert.strictEqual(utils.isReactNative({}), false); }); }); });