glob.test.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. // test src/glob.ts
  6. import { describe, expect, it } from 'vitest';
  7. import { Glob } from '../src/utils/glob';
  8. describe('glob', () => {
  9. describe('isMatch', () => {
  10. it('* at the end', () => {
  11. expect(Glob.isMatch('a.b.*', 'a.b.c')).toBe(true);
  12. expect(Glob.isMatch('a.b.*', 'a.k.c')).toBe(false);
  13. });
  14. it('* at the start', () => {
  15. expect(Glob.isMatch('*.b.c', 'a.b.c')).toBe(true);
  16. expect(Glob.isMatch('*.b.c', 'a.b.x')).toBe(false);
  17. });
  18. it('multiple *', () => {
  19. expect(Glob.isMatch('*.b.*', 'a.b.c')).toBe(true);
  20. expect(Glob.isMatch('a.b.*', 'a.k.c')).toBe(false);
  21. });
  22. it('no *', () => {
  23. expect(Glob.isMatch('a.b.c', 'a.b.c')).toBe(true);
  24. });
  25. it('length not match', () => {
  26. expect(Glob.isMatch('a.b.*', 'a.b.c.c')).toBe(false);
  27. });
  28. });
  29. describe('isMatchOrParent', () => {
  30. it('* at the end', () => {
  31. expect(Glob.isMatchOrParent('a.b.*', 'a.b.c')).toBe(true);
  32. expect(Glob.isMatchOrParent('a.b.*', 'a.k.c')).toBe(false);
  33. });
  34. it('* at the start', () => {
  35. expect(Glob.isMatchOrParent('*.b.c', 'a.b.c')).toBe(true);
  36. expect(Glob.isMatchOrParent('*.b.c', 'a.b.x')).toBe(false);
  37. expect(Glob.isMatchOrParent('*.b', 'a.b.x')).toBe(true);
  38. });
  39. it('multiple *', () => {
  40. expect(Glob.isMatchOrParent('*.b.*', 'a.b.c')).toBe(true);
  41. expect(Glob.isMatchOrParent('*.b.*', 'a.k.c')).toBe(false);
  42. });
  43. it('no *', () => {
  44. expect(Glob.isMatchOrParent('a.b.c', 'a.b.c')).toBe(true);
  45. expect(Glob.isMatchOrParent('a.b', 'a.b.c')).toBe(true);
  46. expect(Glob.isMatchOrParent('a', 'a.b.c')).toBe(true);
  47. expect(Glob.isMatchOrParent('', 'a.b.c.')).toBe(true);
  48. });
  49. it('length not match', () => {
  50. expect(Glob.isMatchOrParent('a.b.*', 'a.b.c.c')).toBe(true);
  51. expect(Glob.isMatchOrParent('a.b.c.d', 'a.b.c.')).toBe(false);
  52. });
  53. });
  54. describe('getParentPathByPattern', () => {
  55. it('should get parent path correctly', () => {
  56. expect(Glob.getParentPathByPattern('a.b.*', 'a.b.c')).toBe('a.b.c');
  57. expect(Glob.getParentPathByPattern('a.b.*', 'a.b.c.d')).toBe('a.b.c');
  58. expect(Glob.getParentPathByPattern('a.b', 'a.b.c.d')).toBe('a.b');
  59. expect(Glob.getParentPathByPattern('a.*.c', 'a.b.c.d')).toBe('a.b.c');
  60. });
  61. });
  62. describe('findMatchPaths', () => {
  63. it('return original path array if no *', () => {
  64. const obj = { a: { b: { c: 1 } } };
  65. expect(Glob.findMatchPaths(obj, 'a.b.c')).toEqual(['a.b.c']);
  66. });
  67. it('object: when * is in middle of the path', () => {
  68. const obj = {
  69. a: { b: { c: 1 } },
  70. x: { y: { z: 2 } },
  71. };
  72. expect(Glob.findMatchPaths(obj, 'a.*.c')).toEqual(['a.b.c']);
  73. });
  74. it('object:when * is at the end of the path', () => {
  75. const obj = {
  76. a: { b: { c: 1 } },
  77. x: { y: { z: 2 } },
  78. };
  79. expect(Glob.findMatchPaths(obj, 'a.*')).toEqual(['a.b']);
  80. });
  81. // 暂时不支持该场景,见glob.ts 中 143行说明
  82. it('object: * 后面数据异构', () => {
  83. const obj = {
  84. a: { b: { c: 1 } },
  85. x: { y: { z: 2 } },
  86. };
  87. expect(Glob.findMatchPaths(obj, '*.y')).toEqual(['x.y']);
  88. });
  89. it('object:when * is at the start and end of the path', () => {
  90. const obj = {
  91. a: { b: { c: 1 } },
  92. x: { y: { z: 2 } },
  93. };
  94. expect(Glob.findMatchPaths(obj, '*.y.*')).toEqual(['x.y.z']);
  95. });
  96. it('array: when * is at the end of the path', () => {
  97. const obj = {
  98. other: 100,
  99. arr: [
  100. {
  101. x: 1,
  102. y: { a: 1, b: 2 },
  103. },
  104. {
  105. x: 10,
  106. y: {
  107. a: 10,
  108. b: 20,
  109. },
  110. },
  111. ],
  112. };
  113. expect(Glob.findMatchPaths(obj, 'arr.*')).toEqual(['arr.0', 'arr.1']);
  114. });
  115. it('array: when * is at the start of the path', () => {
  116. const arr = [
  117. {
  118. x: 1,
  119. y: { a: 1, b: 2 },
  120. },
  121. {
  122. x: 10,
  123. y: {
  124. a: 10,
  125. b: 20,
  126. },
  127. },
  128. ];
  129. expect(Glob.findMatchPaths(arr, '*')).toEqual(['0', '1']);
  130. });
  131. it('array: when * is in the middle of the path', () => {
  132. const obj = {
  133. other: 100,
  134. arr: [
  135. {
  136. x: 1,
  137. y: { a: 1, b: 2 },
  138. },
  139. {
  140. x: 10,
  141. y: {
  142. a: 10,
  143. b: 20,
  144. },
  145. },
  146. ],
  147. };
  148. expect(Glob.findMatchPaths(obj, 'arr.*.y')).toEqual(['arr.0.y', 'arr.1.y']);
  149. });
  150. it('array in array: when double * ', () => {
  151. const obj = {
  152. other: 100,
  153. arr: [
  154. {
  155. x: 1,
  156. y: ['1', '2'],
  157. },
  158. ],
  159. };
  160. expect(Glob.findMatchPaths(obj, 'arr.*.y.*')).toEqual(['arr.0.y.0', 'arr.0.y.1']);
  161. });
  162. it('array in object: when double * ', () => {
  163. const obj = {
  164. x: 100,
  165. y: {
  166. arr: [1, 2],
  167. },
  168. };
  169. expect(Glob.findMatchPaths(obj, 'y.*.*')).toEqual(['y.arr.0', 'y.arr.1']);
  170. });
  171. it('array in object: when double * start ', () => {
  172. const obj = {
  173. x: 100,
  174. y: {
  175. arr: [{ a: 1, b: 2 }],
  176. },
  177. };
  178. expect(Glob.findMatchPaths(obj, '*.arr.*')).toEqual(['y.arr.0']);
  179. });
  180. it('when value after * is empty string ', () => {
  181. const obj = {
  182. $$input_decorator$$: {
  183. inputParameters: [{ name: '', input: 2 }],
  184. },
  185. };
  186. expect(Glob.findMatchPaths(obj, '$$input_decorator$$.inputParameters.*.name')).toEqual([
  187. '$$input_decorator$$.inputParameters.0.name',
  188. ]);
  189. });
  190. it('when value after * is undefined ', () => {
  191. const obj = {
  192. x: {
  193. arr: [{ name: undefined, input: 2 }],
  194. },
  195. };
  196. expect(Glob.findMatchPaths(obj, 'x.arr.*.name')).toEqual(['x.arr.0.name']);
  197. });
  198. it('when value not directly after * is undefined ', () => {
  199. const obj = {
  200. x: {
  201. arr: [{ name: { a: undefined }, input: 2 }],
  202. },
  203. };
  204. expect(Glob.findMatchPaths(obj, 'x.arr.*.name.a')).toEqual(['x.arr.0.name.a']);
  205. });
  206. });
  207. describe('splitPattern', () => {
  208. it('should splict pattern correctly', () => {
  209. expect(Glob.splitPattern('a.b.*.c.*.d')).toEqual(['a.b', '*', 'c', '*', 'd']);
  210. expect(Glob.splitPattern('a.b.*.c.*')).toEqual(['a.b', '*', 'c', '*']);
  211. expect(Glob.splitPattern('a.b.*.*.*.d')).toEqual(['a.b', '*', '*', '*', 'd']);
  212. expect(Glob.splitPattern('*.*.c.*.d')).toEqual(['*', '*', 'c', '*', 'd']);
  213. });
  214. });
  215. describe('getSubPaths', () => {
  216. it('should get sub paths for valid object', () => {
  217. const obj = {
  218. a: {
  219. b: {
  220. x1: {
  221. y1: 1,
  222. },
  223. x2: {
  224. y2: 2,
  225. },
  226. },
  227. },
  228. };
  229. expect(Glob.getSubPaths(['a.b'], obj)).toEqual(['a.b.x1', 'a.b.x2']);
  230. expect(Glob.getSubPaths(['a.b', 'a.b.x1'], obj)).toEqual(['a.b.x1', 'a.b.x2', 'a.b.x1.y1']);
  231. });
  232. it('should get sub paths for array', () => {
  233. const obj = {
  234. a: {
  235. b: {
  236. x1: [1, 2],
  237. },
  238. },
  239. };
  240. expect(Glob.getSubPaths(['a.b.x1'], obj)).toEqual(['a.b.x1.0', 'a.b.x1.1']);
  241. });
  242. it('should get sub paths when root obj is array', () => {
  243. const obj = [
  244. {
  245. x1: [1, 2],
  246. },
  247. {
  248. x2: [1, 2],
  249. },
  250. ];
  251. expect(Glob.getSubPaths(['0.x1'], obj)).toEqual(['0.x1.0', '0.x1.1']);
  252. });
  253. it('should return empty array when obj is not object nor array', () => {
  254. expect(Glob.getSubPaths(['x.y'], 1)).toEqual([]);
  255. expect(Glob.getSubPaths(['x.y'], 'x')).toEqual([]);
  256. expect(Glob.getSubPaths(['x.y'], undefined)).toEqual([]);
  257. });
  258. it('should return empty array when obj has no value for given path', () => {
  259. const obj = {
  260. a: {
  261. b: {
  262. x1: [1, 2],
  263. },
  264. },
  265. };
  266. expect(Glob.getSubPaths(['a.b.c'], obj)).toEqual([]);
  267. });
  268. });
  269. describe('findMatchPathsWithEmptyValue', () => {
  270. it('return original path array if no *', () => {
  271. const obj = { a: { b: { c: 1 } } };
  272. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.b.c')).toEqual(['a.b.c']);
  273. });
  274. it('return original path array if no * and value is empty on multiple layers', () => {
  275. const obj = { a: {} };
  276. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.b.c')).toEqual(['a.b.c']);
  277. });
  278. it('return original path array if no * and value is empty on multiple layers', () => {
  279. const obj = { a: { b: {} } };
  280. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.x.y')).toEqual(['a.x.y']);
  281. });
  282. it('return array with original path even if path does not exists, but the original path does not contain * ', () => {
  283. const obj = { a: { b: { c: 1 } } };
  284. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.b.c.d')).toEqual(['a.b.c.d']);
  285. });
  286. it('return original path array if no * and path related value is undefined in object', () => {
  287. const obj = { a: { b: { c: {} } } };
  288. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.b.c.d')).toEqual(['a.b.c.d']);
  289. });
  290. it('object: when * is in middle of the path', () => {
  291. const obj = {
  292. a: { b: { c: 1 } },
  293. x: { y: { z: 2 } },
  294. };
  295. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.*.c')).toEqual(['a.b.c']);
  296. });
  297. it('object:when * is at the end of the path', () => {
  298. const obj = {
  299. a: { b: { c: 1 } },
  300. x: { y: { z: 2 } },
  301. };
  302. expect(Glob.findMatchPathsWithEmptyValue(obj, 'a.*')).toEqual(['a.b']);
  303. });
  304. // 暂时不支持该场景,见glob.ts 中 143行说明
  305. it('object: * 后面数据异构', () => {
  306. const obj = {
  307. a: { b: { c: 1 } },
  308. x: { y: { z: 2 } },
  309. };
  310. expect(Glob.findMatchPathsWithEmptyValue(obj, '*.y')).toEqual(['a.y', 'x.y']);
  311. });
  312. it('object:when * is at the start and end of the path', () => {
  313. const obj = {
  314. a: { b: { c: 1 } },
  315. x: { y: { z: 2 } },
  316. };
  317. expect(Glob.findMatchPathsWithEmptyValue(obj, '*.y.*')).toEqual(['x.y.z']);
  318. });
  319. it('array: when * is at the end of the path', () => {
  320. const obj = {
  321. other: 100,
  322. arr: [
  323. {
  324. x: 1,
  325. y: { a: 1, b: 2 },
  326. },
  327. {
  328. x: 10,
  329. y: {
  330. a: 10,
  331. b: 20,
  332. },
  333. },
  334. ],
  335. };
  336. expect(Glob.findMatchPathsWithEmptyValue(obj, 'arr.*')).toEqual(['arr.0', 'arr.1']);
  337. });
  338. it('array: when * is at the start of the path', () => {
  339. const arr = [
  340. {
  341. x: 1,
  342. y: { a: 1, b: 2 },
  343. },
  344. {
  345. x: 10,
  346. y: {
  347. a: 10,
  348. b: 20,
  349. },
  350. },
  351. ];
  352. expect(Glob.findMatchPathsWithEmptyValue(arr, '*')).toEqual(['0', '1']);
  353. });
  354. it('array: when * is in the middle of the path', () => {
  355. const obj = {
  356. other: 100,
  357. arr: [
  358. {
  359. x: 1,
  360. y: { a: 1, b: 2 },
  361. },
  362. {
  363. x: 10,
  364. y: {
  365. a: 10,
  366. b: 20,
  367. },
  368. },
  369. ],
  370. };
  371. expect(Glob.findMatchPathsWithEmptyValue(obj, 'arr.*.y')).toEqual(['arr.0.y', 'arr.1.y']);
  372. });
  373. it('array: when data related to path is undefined', () => {
  374. const obj = [{ a: 1 }, { a: 2, b: 3 }];
  375. expect(Glob.findMatchPathsWithEmptyValue(obj, '*.b')).toEqual(['0.b', '1.b']);
  376. });
  377. it('array in array: when double * ', () => {
  378. const obj = {
  379. other: 100,
  380. arr: [
  381. {
  382. x: 1,
  383. y: ['1', '2'],
  384. },
  385. ],
  386. };
  387. expect(Glob.findMatchPathsWithEmptyValue(obj, 'arr.*.y.*')).toEqual([
  388. 'arr.0.y.0',
  389. 'arr.0.y.1',
  390. ]);
  391. });
  392. it('array in object: when double * ', () => {
  393. const obj = {
  394. x: 100,
  395. y: {
  396. arr: [1, 2],
  397. },
  398. };
  399. expect(Glob.findMatchPathsWithEmptyValue(obj, 'y.*.*')).toEqual(['y.arr.0', 'y.arr.1']);
  400. });
  401. it('array in object: when double * start ', () => {
  402. const obj = {
  403. x: 100,
  404. y: {
  405. arr: [{ a: 1, b: 2 }],
  406. },
  407. };
  408. expect(Glob.findMatchPathsWithEmptyValue(obj, '*.arr.*')).toEqual(['y.arr.0']);
  409. });
  410. it('when value after * is empty string ', () => {
  411. const obj = {
  412. $$input_decorator$$: {
  413. inputParameters: [{ name: '', input: 2 }],
  414. },
  415. };
  416. expect(
  417. Glob.findMatchPathsWithEmptyValue(obj, '$$input_decorator$$.inputParameters.*.name')
  418. ).toEqual(['$$input_decorator$$.inputParameters.0.name']);
  419. });
  420. it('when value after * is undefined ', () => {
  421. const obj = {
  422. x: {
  423. arr: [{ name: undefined, input: 2 }],
  424. },
  425. };
  426. expect(Glob.findMatchPathsWithEmptyValue(obj, 'x.arr.*.name')).toEqual(['x.arr.0.name']);
  427. });
  428. it('when value not directly after * is undefined ', () => {
  429. const obj = {
  430. x: {
  431. arr: [{ name: { a: undefined }, input: 2 }],
  432. },
  433. };
  434. expect(Glob.findMatchPathsWithEmptyValue(obj, 'x.arr.*.name.a')).toEqual(['x.arr.0.name.a']);
  435. });
  436. });
  437. });