index.js 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513
  1. // Load modules
  2. var Fs = require('fs');
  3. var Path = require('path');
  4. var Code = require('code');
  5. var Hoek = require('../lib');
  6. var Lab = require('lab');
  7. // Declare internals
  8. var internals = {};
  9. // Test shortcuts
  10. var lab = exports.lab = Lab.script();
  11. var describe = lab.experiment;
  12. var it = lab.test;
  13. var expect = Code.expect;
  14. var nestedObj = {
  15. v: [7, 8, 9],
  16. w: /^something$/igm,
  17. x: {
  18. a: [1, 2, 3],
  19. b: 123456,
  20. c: new Date(),
  21. d: /hi/igm,
  22. e: /hello/
  23. },
  24. y: 'y',
  25. z: new Date(1378775452757)
  26. };
  27. var dupsArray = [nestedObj, { z: 'z' }, nestedObj];
  28. var reducedDupsArray = [nestedObj, { z: 'z' }];
  29. describe('clone()', function () {
  30. it('clones a nested object', function (done) {
  31. var a = nestedObj;
  32. var b = Hoek.clone(a);
  33. expect(a).to.deep.equal(b);
  34. expect(a.z.getTime()).to.equal(b.z.getTime());
  35. done();
  36. });
  37. it('clones a null object', function (done) {
  38. var b = Hoek.clone(null);
  39. expect(b).to.equal(null);
  40. done();
  41. });
  42. it('should not convert undefined properties to null', function (done) {
  43. var obj = { something: undefined };
  44. var b = Hoek.clone(obj);
  45. expect(typeof b.something).to.equal('undefined');
  46. done();
  47. });
  48. it('should not throw on circular reference', function (done) {
  49. var a = {};
  50. a.x = a;
  51. var test = function () {
  52. var b = Hoek.clone(a);
  53. };
  54. expect(test).to.not.throw();
  55. done();
  56. });
  57. it('clones circular reference', function (done) {
  58. var x = {
  59. 'z': new Date()
  60. };
  61. x.y = x;
  62. var b = Hoek.clone(x);
  63. expect(Object.keys(b.y)).to.deep.equal(Object.keys(x));
  64. expect(b.z).to.not.equal(x.z);
  65. expect(b.y).to.not.equal(x.y);
  66. expect(b.y.z).to.not.equal(x.y.z);
  67. expect(b.y).to.equal(b);
  68. expect(b.y.y.y.y).to.equal(b);
  69. done();
  70. });
  71. it('clones an object with a null prototype', function (done) {
  72. var obj = Object.create(null);
  73. var b = Hoek.clone(obj);
  74. expect(b).to.deep.equal(obj);
  75. done();
  76. });
  77. it('clones deeply nested object', function (done) {
  78. var a = {
  79. x: {
  80. y: {
  81. a: [1, 2, 3],
  82. b: 123456,
  83. c: new Date(),
  84. d: /hi/igm,
  85. e: /hello/
  86. }
  87. }
  88. };
  89. var b = Hoek.clone(a);
  90. expect(a).to.deep.equal(b);
  91. expect(a.x.y.c.getTime()).to.equal(b.x.y.c.getTime());
  92. done();
  93. });
  94. it('clones arrays', function (done) {
  95. var a = [1, 2, 3];
  96. var b = Hoek.clone(a);
  97. expect(a).to.deep.equal(b);
  98. done();
  99. });
  100. it('performs actual copy for shallow keys (no pass by reference)', function (done) {
  101. var x = Hoek.clone(nestedObj);
  102. var y = Hoek.clone(nestedObj);
  103. // Date
  104. expect(x.z).to.not.equal(nestedObj.z);
  105. expect(x.z).to.not.equal(y.z);
  106. // Regex
  107. expect(x.w).to.not.equal(nestedObj.w);
  108. expect(x.w).to.not.equal(y.w);
  109. // Array
  110. expect(x.v).to.not.equal(nestedObj.v);
  111. expect(x.v).to.not.equal(y.v);
  112. // Immutable(s)
  113. x.y = 5;
  114. expect(x.y).to.not.equal(nestedObj.y);
  115. expect(x.y).to.not.equal(y.y);
  116. done();
  117. });
  118. it('performs actual copy for deep keys (no pass by reference)', function (done) {
  119. var x = Hoek.clone(nestedObj);
  120. var y = Hoek.clone(nestedObj);
  121. expect(x.x.c).to.not.equal(nestedObj.x.c);
  122. expect(x.x.c).to.not.equal(y.x.c);
  123. expect(x.x.c.getTime()).to.equal(nestedObj.x.c.getTime());
  124. expect(x.x.c.getTime()).to.equal(y.x.c.getTime());
  125. done();
  126. });
  127. it('copies functions with properties', function (done) {
  128. var a = {
  129. x: function () {
  130. return 1;
  131. },
  132. y: {}
  133. };
  134. a.x.z = 'string in function';
  135. a.x.v = function () {
  136. return 2;
  137. };
  138. a.y.u = a.x;
  139. var b = Hoek.clone(a);
  140. expect(b.x()).to.equal(1);
  141. expect(b.x.v()).to.equal(2);
  142. expect(b.y.u).to.equal(b.x);
  143. expect(b.x.z).to.equal('string in function');
  144. done();
  145. });
  146. it('should copy a buffer', function (done) {
  147. var tls = {
  148. key: new Buffer([1, 2, 3, 4, 5]),
  149. cert: new Buffer([1, 2, 3, 4, 5, 6, 10])
  150. };
  151. var copiedTls = Hoek.clone(tls);
  152. expect(Buffer.isBuffer(copiedTls.key)).to.equal(true);
  153. expect(JSON.stringify(copiedTls.key)).to.equal(JSON.stringify(tls.key));
  154. expect(Buffer.isBuffer(copiedTls.cert)).to.equal(true);
  155. expect(JSON.stringify(copiedTls.cert)).to.equal(JSON.stringify(tls.cert));
  156. done();
  157. });
  158. it('clones an object with a prototype', function (done) {
  159. var Obj = function () {
  160. this.a = 5;
  161. };
  162. Obj.prototype.b = function () {
  163. return 'c';
  164. };
  165. var a = new Obj();
  166. var b = Hoek.clone(a);
  167. expect(b.a).to.equal(5);
  168. expect(b.b()).to.equal('c');
  169. expect(a).to.deep.equal(b);
  170. done();
  171. });
  172. it('reuses cloned Date object', function (done) {
  173. var obj = {
  174. a: new Date()
  175. };
  176. obj.b = obj.a;
  177. var copy = Hoek.clone(obj);
  178. expect(copy.a).to.equal(copy.b);
  179. done();
  180. });
  181. it('shallow copies an object with a prototype and isImmutable flag', function (done) {
  182. var Obj = function () {
  183. this.value = 5;
  184. };
  185. Obj.prototype.b = function () {
  186. return 'c';
  187. };
  188. Obj.prototype.isImmutable = true;
  189. var obj = {
  190. a: new Obj()
  191. };
  192. var copy = Hoek.clone(obj);
  193. expect(obj.a.value).to.equal(5);
  194. expect(copy.a.value).to.equal(5);
  195. expect(copy.a.b()).to.equal('c');
  196. expect(obj.a).to.equal(copy.a);
  197. done();
  198. });
  199. it('clones an object with property getter without executing it', function (done) {
  200. var obj = {};
  201. var value = 1;
  202. var execCount = 0;
  203. Object.defineProperty(obj, 'test', {
  204. enumerable: true,
  205. configurable: true,
  206. get: function () {
  207. ++execCount;
  208. return value;
  209. }
  210. });
  211. var copy = Hoek.clone(obj);
  212. expect(execCount).to.equal(0);
  213. expect(copy.test).to.equal(1);
  214. expect(execCount).to.equal(1);
  215. done();
  216. });
  217. it('clones an object with property getter and setter', function (done) {
  218. var obj = {
  219. _test: 0
  220. };
  221. Object.defineProperty(obj, 'test', {
  222. enumerable: true,
  223. configurable: true,
  224. get: function () {
  225. return this._test;
  226. },
  227. set: function (value) {
  228. this._test = value - 1;
  229. }
  230. });
  231. var copy = Hoek.clone(obj);
  232. expect(copy.test).to.equal(0);
  233. copy.test = 5;
  234. expect(copy.test).to.equal(4);
  235. done();
  236. });
  237. it('clones an object with only property setter', function (done) {
  238. var obj = {
  239. _test: 0
  240. };
  241. Object.defineProperty(obj, 'test', {
  242. enumerable: true,
  243. configurable: true,
  244. set: function (value) {
  245. this._test = value - 1;
  246. }
  247. });
  248. var copy = Hoek.clone(obj);
  249. expect(copy._test).to.equal(0);
  250. copy.test = 5;
  251. expect(copy._test).to.equal(4);
  252. done();
  253. });
  254. it('clones an object with non-enumerable properties', function (done) {
  255. var obj = {
  256. _test: 0
  257. };
  258. Object.defineProperty(obj, 'test', {
  259. enumerable: false,
  260. configurable: true,
  261. set: function (value) {
  262. this._test = value - 1;
  263. }
  264. });
  265. var copy = Hoek.clone(obj);
  266. expect(copy._test).to.equal(0);
  267. copy.test = 5;
  268. expect(copy._test).to.equal(4);
  269. done();
  270. });
  271. it('clones an object where getOwnPropertyDescriptor returns undefined', function (done) {
  272. var oldGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
  273. var obj = { a: 'b' };
  274. Object.getOwnPropertyDescriptor = function () {
  275. return undefined;
  276. };
  277. var copy = Hoek.clone(obj);
  278. Object.getOwnPropertyDescriptor = oldGetOwnPropertyDescriptor;
  279. expect(copy).to.deep.equal(obj);
  280. done();
  281. });
  282. });
  283. describe('merge()', function () {
  284. it('deep copies source items', function (done) {
  285. var target = {
  286. b: 3,
  287. d: []
  288. };
  289. var source = {
  290. c: {
  291. d: 1
  292. },
  293. d: [{ e: 1 }]
  294. };
  295. Hoek.merge(target, source);
  296. expect(target.c).to.not.equal(source.c);
  297. expect(target.c).to.deep.equal(source.c);
  298. expect(target.d).to.not.equal(source.d);
  299. expect(target.d[0]).to.not.equal(source.d[0]);
  300. expect(target.d).to.deep.equal(source.d);
  301. done();
  302. });
  303. it('merges array over an object', function (done) {
  304. var a = {
  305. x: ['n', 'm']
  306. };
  307. var b = {
  308. x: {
  309. n: '1',
  310. m: '2'
  311. }
  312. };
  313. Hoek.merge(b, a);
  314. expect(a.x[0]).to.equal('n');
  315. expect(a.x.n).to.not.exist();
  316. done();
  317. });
  318. it('merges object over an array', function (done) {
  319. var a = {
  320. x: ['n', 'm']
  321. };
  322. var b = {
  323. x: {
  324. n: '1',
  325. m: '2'
  326. }
  327. };
  328. Hoek.merge(a, b);
  329. expect(a.x.n).to.equal('1');
  330. expect(a.x[0]).to.not.exist();
  331. done();
  332. });
  333. it('does not throw if source is null', function (done) {
  334. var a = {};
  335. var b = null;
  336. var c = null;
  337. expect(function () {
  338. c = Hoek.merge(a, b);
  339. }).to.not.throw();
  340. expect(c).to.equal(a);
  341. done();
  342. });
  343. it('does not throw if source is undefined', function (done) {
  344. var a = {};
  345. var b;
  346. var c = null;
  347. expect(function () {
  348. c = Hoek.merge(a, b);
  349. }).to.not.throw();
  350. expect(c).to.equal(a);
  351. done();
  352. });
  353. it('throws if source is not an object', function (done) {
  354. expect(function () {
  355. var a = {};
  356. var b = 0;
  357. Hoek.merge(a, b);
  358. }).to.throw('Invalid source value: must be null, undefined, or an object');
  359. done();
  360. });
  361. it('throws if target is not an object', function (done) {
  362. expect(function () {
  363. var a = 0;
  364. var b = {};
  365. Hoek.merge(a, b);
  366. }).to.throw('Invalid target value: must be an object');
  367. done();
  368. });
  369. it('throws if target is not an array and source is', function (done) {
  370. expect(function () {
  371. var a = {};
  372. var b = [1, 2];
  373. Hoek.merge(a, b);
  374. }).to.throw('Cannot merge array onto an object');
  375. done();
  376. });
  377. it('returns the same object when merging arrays', function (done) {
  378. var a = [];
  379. var b = [1, 2];
  380. expect(Hoek.merge(a, b)).to.equal(a);
  381. done();
  382. });
  383. it('combines an empty object with a non-empty object', function (done) {
  384. var a = {};
  385. var b = nestedObj;
  386. var c = Hoek.merge(a, b);
  387. expect(a).to.deep.equal(b);
  388. expect(c).to.deep.equal(b);
  389. done();
  390. });
  391. it('overrides values in target', function (done) {
  392. var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' };
  393. var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' };
  394. var c = Hoek.merge(a, b);
  395. expect(c.x).to.equal(null);
  396. expect(c.y).to.equal(2);
  397. expect(c.z).to.equal(4);
  398. expect(c.v).to.equal(0);
  399. expect(c.m).to.equal('123');
  400. expect(c.t).to.deep.equal({ u: 6 });
  401. done();
  402. });
  403. it('overrides values in target (flip)', function (done) {
  404. var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' };
  405. var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' };
  406. var d = Hoek.merge(b, a);
  407. expect(d.x).to.equal(1);
  408. expect(d.y).to.equal(2);
  409. expect(d.z).to.equal(3);
  410. expect(d.v).to.equal(5);
  411. expect(d.m).to.equal('abc');
  412. expect(d.t).to.deep.equal('test');
  413. done();
  414. });
  415. it('retains Date properties', function (done) {
  416. var a = { x: new Date(1378776452757) };
  417. var b = Hoek.merge({}, a);
  418. expect(a.x.getTime()).to.equal(b.x.getTime());
  419. done();
  420. });
  421. it('retains Date properties when merging keys', function (done) {
  422. var a = { x: new Date(1378776452757) };
  423. var b = Hoek.merge({ x: {} }, a);
  424. expect(a.x.getTime()).to.equal(b.x.getTime());
  425. done();
  426. });
  427. it('overrides Buffer', function (done) {
  428. var a = { x: new Buffer('abc') };
  429. var b = Hoek.merge({ x: {} }, a);
  430. expect(a.x.toString()).to.equal('abc');
  431. done();
  432. });
  433. });
  434. describe('applyToDefaults()', function () {
  435. var defaults = {
  436. a: 1,
  437. b: 2,
  438. c: {
  439. d: 3,
  440. e: [5, 6]
  441. },
  442. f: 6,
  443. g: 'test'
  444. };
  445. it('throws when target is null', function (done) {
  446. expect(function () {
  447. Hoek.applyToDefaults(null, {});
  448. }).to.throw('Invalid defaults value: must be an object');
  449. done();
  450. });
  451. it('returns null if options is false', function (done) {
  452. var result = Hoek.applyToDefaults(defaults, false);
  453. expect(result).to.equal(null);
  454. done();
  455. });
  456. it('returns null if options is null', function (done) {
  457. var result = Hoek.applyToDefaults(defaults, null);
  458. expect(result).to.equal(null);
  459. done();
  460. });
  461. it('returns null if options is undefined', function (done) {
  462. var result = Hoek.applyToDefaults(defaults, undefined);
  463. expect(result).to.equal(null);
  464. done();
  465. });
  466. it('returns a copy of defaults if options is true', function (done) {
  467. var result = Hoek.applyToDefaults(defaults, true);
  468. expect(result).to.deep.equal(defaults);
  469. done();
  470. });
  471. it('applies object to defaults', function (done) {
  472. var obj = {
  473. a: null,
  474. c: {
  475. e: [4]
  476. },
  477. f: 0,
  478. g: {
  479. h: 5
  480. }
  481. };
  482. var result = Hoek.applyToDefaults(defaults, obj);
  483. expect(result.c.e).to.deep.equal([4]);
  484. expect(result.a).to.equal(1);
  485. expect(result.b).to.equal(2);
  486. expect(result.f).to.equal(0);
  487. expect(result.g).to.deep.equal({ h: 5 });
  488. done();
  489. });
  490. it('applies object to defaults with null', function (done) {
  491. var obj = {
  492. a: null,
  493. c: {
  494. e: [4]
  495. },
  496. f: 0,
  497. g: {
  498. h: 5
  499. }
  500. };
  501. var result = Hoek.applyToDefaults(defaults, obj, true);
  502. expect(result.c.e).to.deep.equal([4]);
  503. expect(result.a).to.equal(null);
  504. expect(result.b).to.equal(2);
  505. expect(result.f).to.equal(0);
  506. expect(result.g).to.deep.equal({ h: 5 });
  507. done();
  508. });
  509. });
  510. describe('cloneWithShallow()', function () {
  511. it('deep clones except for listed keys', function (done) {
  512. var source = {
  513. a: {
  514. b: 5
  515. },
  516. c: {
  517. d: 6
  518. }
  519. };
  520. var copy = Hoek.cloneWithShallow(source, ['c']);
  521. expect(copy).to.deep.equal(source);
  522. expect(copy).to.not.equal(source);
  523. expect(copy.a).to.not.equal(source.a);
  524. expect(copy.b).to.equal(source.b);
  525. done();
  526. });
  527. it('returns immutable value', function (done) {
  528. expect(Hoek.cloneWithShallow(5)).to.equal(5);
  529. done();
  530. });
  531. it('returns null value', function (done) {
  532. expect(Hoek.cloneWithShallow(null)).to.equal(null);
  533. done();
  534. });
  535. it('returns undefined value', function (done) {
  536. expect(Hoek.cloneWithShallow(undefined)).to.equal(undefined);
  537. done();
  538. });
  539. it('deep clones except for listed keys (including missing keys)', function (done) {
  540. var source = {
  541. a: {
  542. b: 5
  543. },
  544. c: {
  545. d: 6
  546. }
  547. };
  548. var copy = Hoek.cloneWithShallow(source, ['c', 'v']);
  549. expect(copy).to.deep.equal(source);
  550. expect(copy).to.not.equal(source);
  551. expect(copy.a).to.not.equal(source.a);
  552. expect(copy.b).to.equal(source.b);
  553. done();
  554. });
  555. });
  556. describe('applyToDefaultsWithShallow()', function () {
  557. it('shallow copies the listed keys from options without merging', function (done) {
  558. var defaults = {
  559. a: {
  560. b: 5,
  561. e: 3
  562. },
  563. c: {
  564. d: 7,
  565. g: 1
  566. }
  567. };
  568. var options = {
  569. a: {
  570. b: 4
  571. },
  572. c: {
  573. d: 6,
  574. f: 7
  575. }
  576. };
  577. var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['a']);
  578. expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: 1, f: 7 } });
  579. expect(merged.a).to.equal(options.a);
  580. expect(merged.a).to.not.equal(defaults.a);
  581. expect(merged.c).to.not.equal(options.c);
  582. expect(merged.c).to.not.equal(defaults.c);
  583. done();
  584. });
  585. it('shallow copies the nested keys (override)', function (done) {
  586. var defaults = {
  587. a: {
  588. b: 5
  589. },
  590. c: {
  591. d: 7,
  592. g: 1
  593. }
  594. };
  595. var options = {
  596. a: {
  597. b: 4
  598. },
  599. c: {
  600. d: 6,
  601. g: {
  602. h: 8
  603. }
  604. }
  605. };
  606. var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
  607. expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: { h: 8 } } });
  608. expect(merged.c.g).to.equal(options.c.g);
  609. done();
  610. });
  611. it('shallow copies the nested keys (missing)', function (done) {
  612. var defaults = {
  613. a: {
  614. b: 5
  615. }
  616. };
  617. var options = {
  618. a: {
  619. b: 4
  620. },
  621. c: {
  622. g: {
  623. h: 8
  624. }
  625. }
  626. };
  627. var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
  628. expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } });
  629. expect(merged.c.g).to.equal(options.c.g);
  630. done();
  631. });
  632. it('shallow copies the nested keys (override)', function (done) {
  633. var defaults = {
  634. a: {
  635. b: 5
  636. },
  637. c: {
  638. g: {
  639. d: 7
  640. }
  641. }
  642. };
  643. var options = {
  644. a: {
  645. b: 4
  646. },
  647. c: {
  648. g: {
  649. h: 8
  650. }
  651. }
  652. };
  653. var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
  654. expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } });
  655. expect(merged.c.g).to.equal(options.c.g);
  656. done();
  657. });
  658. it('shallow copies the nested keys (deeper)', function (done) {
  659. var defaults = {
  660. a: {
  661. b: 5
  662. }
  663. };
  664. var options = {
  665. a: {
  666. b: 4
  667. },
  668. c: {
  669. g: {
  670. r: {
  671. h: 8
  672. }
  673. }
  674. }
  675. };
  676. var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g.r']);
  677. expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } });
  678. expect(merged.c.g.r).to.equal(options.c.g.r);
  679. done();
  680. });
  681. it('shallow copies the nested keys (not present)', function (done) {
  682. var defaults = {
  683. a: {
  684. b: 5
  685. }
  686. };
  687. var options = {
  688. a: {
  689. b: 4
  690. },
  691. c: {
  692. g: {
  693. r: {
  694. h: 8
  695. }
  696. }
  697. }
  698. };
  699. var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['x.y']);
  700. expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } });
  701. done();
  702. });
  703. it('shallow copies the listed keys in the defaults', function (done) {
  704. var defaults = {
  705. a: {
  706. b: 1
  707. }
  708. };
  709. var merged = Hoek.applyToDefaultsWithShallow(defaults, {}, ['a']);
  710. expect(merged.a).to.equal(defaults.a);
  711. done();
  712. });
  713. it('shallow copies the listed keys in the defaults (true)', function (done) {
  714. var defaults = {
  715. a: {
  716. b: 1
  717. }
  718. };
  719. var merged = Hoek.applyToDefaultsWithShallow(defaults, true, ['a']);
  720. expect(merged.a).to.equal(defaults.a);
  721. done();
  722. });
  723. it('returns null on false', function (done) {
  724. var defaults = {
  725. a: {
  726. b: 1
  727. }
  728. };
  729. var merged = Hoek.applyToDefaultsWithShallow(defaults, false, ['a']);
  730. expect(merged).to.equal(null);
  731. done();
  732. });
  733. it('throws on missing defaults', function (done) {
  734. expect(function () {
  735. Hoek.applyToDefaultsWithShallow(null, {}, ['a']);
  736. }).to.throw('Invalid defaults value: must be an object');
  737. done();
  738. });
  739. it('throws on invalid defaults', function (done) {
  740. expect(function () {
  741. Hoek.applyToDefaultsWithShallow('abc', {}, ['a']);
  742. }).to.throw('Invalid defaults value: must be an object');
  743. done();
  744. });
  745. it('throws on invalid options', function (done) {
  746. expect(function () {
  747. Hoek.applyToDefaultsWithShallow({}, 'abc', ['a']);
  748. }).to.throw('Invalid options value: must be true, falsy or an object');
  749. done();
  750. });
  751. it('throws on missing keys', function (done) {
  752. expect(function () {
  753. Hoek.applyToDefaultsWithShallow({}, true);
  754. }).to.throw('Invalid keys');
  755. done();
  756. });
  757. it('throws on invalid keys', function (done) {
  758. expect(function () {
  759. Hoek.applyToDefaultsWithShallow({}, true, 'a');
  760. }).to.throw('Invalid keys');
  761. done();
  762. });
  763. });
  764. describe('deepEqual()', function () {
  765. it('compares simple values', function (done) {
  766. expect(Hoek.deepEqual('x', 'x')).to.be.true();
  767. expect(Hoek.deepEqual('x', 'y')).to.be.false();
  768. expect(Hoek.deepEqual('x1', 'x')).to.be.false();
  769. expect(Hoek.deepEqual(-0, +0)).to.be.false();
  770. expect(Hoek.deepEqual(-0, -0)).to.be.true();
  771. expect(Hoek.deepEqual(+0, +0)).to.be.true();
  772. expect(Hoek.deepEqual(+0, -0)).to.be.false();
  773. expect(Hoek.deepEqual(1, 1)).to.be.true();
  774. expect(Hoek.deepEqual(0, 0)).to.be.true();
  775. expect(Hoek.deepEqual(-1, 1)).to.be.false();
  776. expect(Hoek.deepEqual(NaN, 0)).to.be.false();
  777. expect(Hoek.deepEqual(NaN, NaN)).to.be.true();
  778. done();
  779. });
  780. it('compares different types', function (done) {
  781. expect(Hoek.deepEqual([], 5)).to.be.false();
  782. expect(Hoek.deepEqual(5, [])).to.be.false();
  783. expect(Hoek.deepEqual({}, null)).to.be.false();
  784. expect(Hoek.deepEqual(null, {})).to.be.false();
  785. expect(Hoek.deepEqual('abc', {})).to.be.false();
  786. expect(Hoek.deepEqual({}, 'abc')).to.be.false();
  787. done();
  788. });
  789. it('compares empty structures', function (done) {
  790. expect(Hoek.deepEqual([], [])).to.be.true();
  791. expect(Hoek.deepEqual({}, {})).to.be.true();
  792. expect(Hoek.deepEqual([], {})).to.be.false();
  793. done();
  794. });
  795. it('compares empty arguments object', function (done) {
  796. var compare = function () {
  797. expect(Hoek.deepEqual([], arguments)).to.be.false();
  798. };
  799. compare();
  800. done();
  801. });
  802. it('compares empty arguments objects', function (done) {
  803. var compare = function () {
  804. var arg1 = arguments;
  805. var inner = function () {
  806. expect(Hoek.deepEqual(arg1, arguments)).to.be.false(); // callee is not the same
  807. };
  808. inner();
  809. };
  810. compare();
  811. done();
  812. });
  813. it('compares dates', function (done) {
  814. expect(Hoek.deepEqual(new Date(2015, 1, 1), new Date(2015, 1, 1))).to.be.true();
  815. expect(Hoek.deepEqual(new Date(100), new Date(101))).to.be.false();
  816. expect(Hoek.deepEqual(new Date(), {})).to.be.false();
  817. done();
  818. });
  819. it('compares regular expressions', function (done) {
  820. expect(Hoek.deepEqual(/\s/, new RegExp('\\\s'))).to.be.true();
  821. expect(Hoek.deepEqual(/\s/g, /\s/g)).to.be.true();
  822. expect(Hoek.deepEqual(/a/, {})).to.be.false();
  823. expect(Hoek.deepEqual(/\s/g, /\s/i)).to.be.false();
  824. expect(Hoek.deepEqual(/a/g, /b/g)).to.be.false();
  825. done();
  826. });
  827. it('compares arrays', function (done) {
  828. expect(Hoek.deepEqual([[1]], [[1]])).to.be.true();
  829. expect(Hoek.deepEqual([1, 2, 3], [1, 2, 3])).to.be.true();
  830. expect(Hoek.deepEqual([1, 2, 3], [1, 3, 2])).to.be.false();
  831. expect(Hoek.deepEqual([1, 2, 3], [1, 2])).to.be.false();
  832. expect(Hoek.deepEqual([1], [1])).to.be.true();
  833. done();
  834. });
  835. it('compares buffers', function (done) {
  836. expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2, 3]))).to.be.true();
  837. expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 3, 2]))).to.be.false();
  838. expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2]))).to.be.false();
  839. expect(Hoek.deepEqual(new Buffer([1, 2, 3]), {})).to.be.false();
  840. expect(Hoek.deepEqual(new Buffer([1, 2, 3]), [1, 2, 3])).to.be.false();
  841. done();
  842. });
  843. it('compares objects', function (done) {
  844. expect(Hoek.deepEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).to.be.true();
  845. expect(Hoek.deepEqual({ foo: 'bar' }, { foo: 'baz' })).to.be.false();
  846. expect(Hoek.deepEqual({ foo: { bar: 'foo' } }, { foo: { bar: 'baz' } })).to.be.false();
  847. done();
  848. });
  849. it('handles circular dependency', function (done) {
  850. var a = {};
  851. a.x = a;
  852. var b = Hoek.clone(a);
  853. expect(Hoek.deepEqual(a, b)).to.be.true();
  854. done();
  855. });
  856. it('compares an object with property getter without executing it', function (done) {
  857. var obj = {};
  858. var value = 1;
  859. var execCount = 0;
  860. Object.defineProperty(obj, 'test', {
  861. enumerable: true,
  862. configurable: true,
  863. get: function () {
  864. ++execCount;
  865. return value;
  866. }
  867. });
  868. var copy = Hoek.clone(obj);
  869. expect(Hoek.deepEqual(obj, copy)).to.be.true();
  870. expect(execCount).to.equal(0);
  871. expect(copy.test).to.equal(1);
  872. expect(execCount).to.equal(1);
  873. done();
  874. });
  875. it('compares objects with property getters', function (done) {
  876. var obj = {};
  877. Object.defineProperty(obj, 'test', {
  878. enumerable: true,
  879. configurable: true,
  880. get: function () {
  881. return 1;
  882. }
  883. });
  884. var ref = {};
  885. Object.defineProperty(ref, 'test', {
  886. enumerable: true,
  887. configurable: true,
  888. get: function () {
  889. return 2;
  890. }
  891. });
  892. expect(Hoek.deepEqual(obj, ref)).to.be.false();
  893. done();
  894. });
  895. it('compares object prototypes', function (done) {
  896. var Obj = function () {
  897. this.a = 5;
  898. };
  899. Obj.prototype.b = function () {
  900. return this.a;
  901. };
  902. var Ref = function () {
  903. this.a = 5;
  904. };
  905. Ref.prototype.b = function () {
  906. return this.a;
  907. };
  908. expect(Hoek.deepEqual(new Obj(), new Ref())).to.be.false();
  909. expect(Hoek.deepEqual(new Obj(), new Obj())).to.be.true();
  910. expect(Hoek.deepEqual(new Ref(), new Ref())).to.be.true();
  911. done();
  912. });
  913. it('compares plain objects', function (done) {
  914. var a = Object.create(null);
  915. var b = Object.create(null);
  916. a.b = 'c';
  917. b.b = 'c';
  918. expect(Hoek.deepEqual(a, b)).to.be.true();
  919. expect(Hoek.deepEqual(a, { b: 'c' })).to.be.false();
  920. done();
  921. });
  922. it('compares an object with an empty object', function (done) {
  923. var a = { a: 1, b: 2 };
  924. expect(Hoek.deepEqual({}, a)).to.be.false();
  925. expect(Hoek.deepEqual(a, {})).to.be.false();
  926. done();
  927. });
  928. it('compares an object ignoring the prototype', function (done) {
  929. var a = Object.create(null);
  930. var b = {};
  931. expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true();
  932. done();
  933. });
  934. it('compares an object ignoring the prototype recursively', function (done) {
  935. var a = [Object.create(null)];
  936. var b = [{}];
  937. expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true();
  938. done();
  939. });
  940. });
  941. describe('unique()', function () {
  942. it('ensures uniqueness within array of objects based on subkey', function (done) {
  943. var a = Hoek.unique(dupsArray, 'x');
  944. expect(a).to.deep.equal(reducedDupsArray);
  945. done();
  946. });
  947. it('removes duplicated without key', function (done) {
  948. expect(Hoek.unique([1, 2, 3, 4, 2, 1, 5])).to.deep.equal([1, 2, 3, 4, 5]);
  949. done();
  950. });
  951. });
  952. describe('mapToObject()', function () {
  953. it('returns null on null array', function (done) {
  954. var a = Hoek.mapToObject(null);
  955. expect(a).to.equal(null);
  956. done();
  957. });
  958. it('converts basic array to existential object', function (done) {
  959. var keys = [1, 2, 3, 4];
  960. var a = Hoek.mapToObject(keys);
  961. for (var i in keys) {
  962. expect(a[keys[i]]).to.equal(true);
  963. }
  964. done();
  965. });
  966. it('converts array of objects to existential object', function (done) {
  967. var keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }];
  968. var subkey = 'x';
  969. var a = Hoek.mapToObject(keys, subkey);
  970. expect(a).to.deep.equal({ 1: true, 2: true, 3: true });
  971. done();
  972. });
  973. });
  974. describe('intersect()', function () {
  975. it('returns the common objects of two arrays', function (done) {
  976. var array1 = [1, 2, 3, 4, 4, 5, 5];
  977. var array2 = [5, 4, 5, 6, 7];
  978. var common = Hoek.intersect(array1, array2);
  979. expect(common.length).to.equal(2);
  980. done();
  981. });
  982. it('returns just the first common object of two arrays', function (done) {
  983. var array1 = [1, 2, 3, 4, 4, 5, 5];
  984. var array2 = [5, 4, 5, 6, 7];
  985. var common = Hoek.intersect(array1, array2, true);
  986. expect(common).to.equal(5);
  987. done();
  988. });
  989. it('returns null when no common and returning just the first common object of two arrays', function (done) {
  990. var array1 = [1, 2, 3, 4, 4, 5, 5];
  991. var array2 = [6, 7];
  992. var common = Hoek.intersect(array1, array2, true);
  993. expect(common).to.equal(null);
  994. done();
  995. });
  996. it('returns an empty array if either input is null', function (done) {
  997. expect(Hoek.intersect([1], null).length).to.equal(0);
  998. expect(Hoek.intersect(null, [1]).length).to.equal(0);
  999. done();
  1000. });
  1001. it('returns the common objects of object and array', function (done) {
  1002. var array1 = [1, 2, 3, 4, 4, 5, 5];
  1003. var array2 = [5, 4, 5, 6, 7];
  1004. var common = Hoek.intersect(Hoek.mapToObject(array1), array2);
  1005. expect(common.length).to.equal(2);
  1006. done();
  1007. });
  1008. });
  1009. describe('contain()', function () {
  1010. it('tests strings', function (done) {
  1011. expect(Hoek.contain('abc', 'ab')).to.be.true();
  1012. expect(Hoek.contain('abc', 'abc', { only: true })).to.be.true();
  1013. expect(Hoek.contain('aaa', 'a', { only: true })).to.be.true();
  1014. expect(Hoek.contain('abc', 'b', { once: true })).to.be.true();
  1015. expect(Hoek.contain('abc', ['a', 'c'])).to.be.true();
  1016. expect(Hoek.contain('abc', ['a', 'd'], { part: true })).to.be.true();
  1017. expect(Hoek.contain('abc', 'ac')).to.be.false();
  1018. expect(Hoek.contain('abcd', 'abc', { only: true })).to.be.false();
  1019. expect(Hoek.contain('aab', 'a', { only: true })).to.be.false();
  1020. expect(Hoek.contain('abb', 'b', { once: true })).to.be.false();
  1021. expect(Hoek.contain('abc', ['a', 'd'])).to.be.false();
  1022. expect(Hoek.contain('abc', ['ab', 'bc'])).to.be.false(); // Overlapping values not supported
  1023. done();
  1024. });
  1025. it('tests arrays', function (done) {
  1026. expect(Hoek.contain([1, 2, 3], 1)).to.be.true();
  1027. expect(Hoek.contain([{ a: 1 }], { a: 1 }, { deep: true })).to.be.true();
  1028. expect(Hoek.contain([1, 2, 3], [1, 2])).to.be.true();
  1029. expect(Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true })).to.be.true();
  1030. expect(Hoek.contain([1, 1, 2], [1, 2], { only: true })).to.be.true();
  1031. expect(Hoek.contain([1, 2], [1, 2], { once: true })).to.be.true();
  1032. expect(Hoek.contain([1, 2, 3], [1, 4], { part: true })).to.be.true();
  1033. expect(Hoek.contain([[1], [2]], [[1]], { deep: true })).to.be.true();
  1034. expect(Hoek.contain([1, 2, 3], 4)).to.be.false();
  1035. expect(Hoek.contain([{ a: 1 }], { a: 2 }, { deep: true })).to.be.false();
  1036. expect(Hoek.contain([{ a: 1 }], { a: 1 })).to.be.false();
  1037. expect(Hoek.contain([1, 2, 3], [4, 5])).to.be.false();
  1038. expect(Hoek.contain([[3], [2]], [[1]])).to.be.false();
  1039. expect(Hoek.contain([[1], [2]], [[1]])).to.be.false();
  1040. expect(Hoek.contain([{ a: 1 }], [{ a: 2 }], { deep: true })).to.be.false();
  1041. expect(Hoek.contain([1, 3, 2], [1, 2], { only: true })).to.be.false();
  1042. expect(Hoek.contain([1, 2, 2], [1, 2], { once: true })).to.be.false();
  1043. expect(Hoek.contain([0, 2, 3], [1, 4], { part: true })).to.be.false();
  1044. done();
  1045. });
  1046. it('tests objects', function (done) {
  1047. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'a')).to.be.true();
  1048. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'c'])).to.be.true();
  1049. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'b', 'c'], { only: true })).to.be.true();
  1050. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1 })).to.be.true();
  1051. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, c: 3 })).to.be.true();
  1052. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true })).to.be.true();
  1053. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, { only: true })).to.be.true();
  1054. expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] }, { deep: true })).to.be.true();
  1055. expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true })).to.be.true();
  1056. expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: true })).to.be.true();
  1057. expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: false })).to.be.false();
  1058. expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: true })).to.be.false();
  1059. expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: false })).to.be.true();
  1060. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'd')).to.be.false();
  1061. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'd'])).to.be.false();
  1062. expect(Hoek.contain({ a: 1, b: 2, c: 3, d: 4 }, ['a', 'b', 'c'], { only: true })).to.be.false();
  1063. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2 })).to.be.false();
  1064. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2, b: 2 }, { part: true })).to.be.false(); // part does not ignore bad value
  1065. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 3 })).to.be.false();
  1066. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 })).to.be.false();
  1067. expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 }, { only: true })).to.be.false();
  1068. expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] })).to.be.false();
  1069. expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } })).to.be.false();
  1070. expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true })).to.be.true();
  1071. expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: true })).to.be.false();
  1072. expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: false })).to.be.true();
  1073. expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: true })).to.be.true();
  1074. expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: false })).to.be.false();
  1075. // Getter check
  1076. var Foo = function (bar) {
  1077. this.bar = bar;
  1078. };
  1079. Object.defineProperty(Foo.prototype, 'baz', {
  1080. enumerable: true,
  1081. get: function () {
  1082. return this.bar;
  1083. }
  1084. });
  1085. expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true })).to.be.true();
  1086. expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true, part: true })).to.be.true();
  1087. expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true })).to.be.true();
  1088. expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, only: true })).to.be.false();
  1089. expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: false })).to.be.false();
  1090. expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: true })).to.be.true();
  1091. done();
  1092. });
  1093. });
  1094. describe('flatten()', function () {
  1095. it('returns a flat array', function (done) {
  1096. var result = Hoek.flatten([1, 2, [3, 4, [5, 6], [7], 8], [9], [10, [11, 12]], 13]);
  1097. expect(result.length).to.equal(13);
  1098. expect(result).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
  1099. done();
  1100. });
  1101. });
  1102. describe('reach()', function () {
  1103. var obj = {
  1104. a: {
  1105. b: {
  1106. c: {
  1107. d: 1,
  1108. e: 2
  1109. },
  1110. f: 'hello'
  1111. },
  1112. g: {
  1113. h: 3
  1114. }
  1115. },
  1116. i: function () { },
  1117. j: null,
  1118. k: [4, 8, 9, 1]
  1119. };
  1120. obj.i.x = 5;
  1121. it('returns object itself', function (done) {
  1122. expect(Hoek.reach(obj, null)).to.equal(obj);
  1123. expect(Hoek.reach(obj, false)).to.equal(obj);
  1124. expect(Hoek.reach(obj)).to.equal(obj);
  1125. done();
  1126. });
  1127. it('returns first value of array', function (done) {
  1128. expect(Hoek.reach(obj, 'k.0')).to.equal(4);
  1129. done();
  1130. });
  1131. it('returns last value of array using negative index', function (done) {
  1132. expect(Hoek.reach(obj, 'k.-2')).to.equal(9);
  1133. done();
  1134. });
  1135. it('returns a valid member', function (done) {
  1136. expect(Hoek.reach(obj, 'a.b.c.d')).to.equal(1);
  1137. done();
  1138. });
  1139. it('returns a valid member with separator override', function (done) {
  1140. expect(Hoek.reach(obj, 'a/b/c/d', '/')).to.equal(1);
  1141. done();
  1142. });
  1143. it('returns undefined on null object', function (done) {
  1144. expect(Hoek.reach(null, 'a.b.c.d')).to.equal(undefined);
  1145. done();
  1146. });
  1147. it('returns undefined on missing object member', function (done) {
  1148. expect(Hoek.reach(obj, 'a.b.c.d.x')).to.equal(undefined);
  1149. done();
  1150. });
  1151. it('returns undefined on missing function member', function (done) {
  1152. expect(Hoek.reach(obj, 'i.y', { functions: true })).to.equal(undefined);
  1153. done();
  1154. });
  1155. it('throws on missing member in strict mode', function (done) {
  1156. expect(function () {
  1157. Hoek.reach(obj, 'a.b.c.o.x', { strict: true });
  1158. }).to.throw('Missing segment o in reach path a.b.c.o.x');
  1159. done();
  1160. });
  1161. it('returns undefined on invalid member', function (done) {
  1162. expect(Hoek.reach(obj, 'a.b.c.d-.x')).to.equal(undefined);
  1163. done();
  1164. });
  1165. it('returns function member', function (done) {
  1166. expect(typeof Hoek.reach(obj, 'i')).to.equal('function');
  1167. done();
  1168. });
  1169. it('returns function property', function (done) {
  1170. expect(Hoek.reach(obj, 'i.x')).to.equal(5);
  1171. done();
  1172. });
  1173. it('returns null', function (done) {
  1174. expect(Hoek.reach(obj, 'j')).to.equal(null);
  1175. done();
  1176. });
  1177. it('throws on function property when functions not allowed', function (done) {
  1178. expect(function () {
  1179. Hoek.reach(obj, 'i.x', { functions: false });
  1180. }).to.throw('Invalid segment x in reach path i.x');
  1181. done();
  1182. });
  1183. it('will return a default value if property is not found', function (done) {
  1184. expect(Hoek.reach(obj, 'a.b.q', { default: 'defaultValue' })).to.equal('defaultValue');
  1185. done();
  1186. });
  1187. it('will return a default value if path is not found', function (done) {
  1188. expect(Hoek.reach(obj, 'q', { default: 'defaultValue' })).to.equal('defaultValue');
  1189. done();
  1190. });
  1191. it('allows a falsey value to be used as the default value', function (done) {
  1192. expect(Hoek.reach(obj, 'q', { default: '' })).to.equal('');
  1193. done();
  1194. });
  1195. });
  1196. describe('reachTemplate()', function () {
  1197. it('applies object to template', function (done) {
  1198. var obj = {
  1199. a: {
  1200. b: {
  1201. c: {
  1202. d: 1
  1203. }
  1204. }
  1205. },
  1206. j: null,
  1207. k: [4, 8, 9, 1]
  1208. };
  1209. var template = '{k.0}:{k.-2}:{a.b.c.d}:{x.y}:{j}';
  1210. expect(Hoek.reachTemplate(obj, template)).to.equal('4:9:1::');
  1211. done();
  1212. });
  1213. it('applies object to template (options)', function (done) {
  1214. var obj = {
  1215. a: {
  1216. b: {
  1217. c: {
  1218. d: 1
  1219. }
  1220. }
  1221. },
  1222. j: null,
  1223. k: [4, 8, 9, 1]
  1224. };
  1225. var template = '{k/0}:{k/-2}:{a/b/c/d}:{x/y}:{j}';
  1226. expect(Hoek.reachTemplate(obj, template, '/')).to.equal('4:9:1::');
  1227. done();
  1228. });
  1229. });
  1230. describe('callStack()', function () {
  1231. it('returns the full call stack', function (done) {
  1232. var stack = Hoek.callStack();
  1233. expect(stack[0][0]).to.contain('index.js');
  1234. expect(stack[0][2]).to.equal(26);
  1235. done();
  1236. });
  1237. });
  1238. describe('displayStack ()', function () {
  1239. it('returns the full call stack for display', function (done) {
  1240. var stack = Hoek.displayStack();
  1241. expect(stack[0]).to.contain(Path.normalize('/test/index.js') + ':');
  1242. done();
  1243. });
  1244. it('includes constructor functions correctly', function (done) {
  1245. var Something = function (next) {
  1246. next();
  1247. };
  1248. var something = new Something(function () {
  1249. var stack = Hoek.displayStack();
  1250. expect(stack[1]).to.contain('new Something');
  1251. done();
  1252. });
  1253. });
  1254. });
  1255. describe('abort()', function () {
  1256. it('exits process when not in test mode', function (done) {
  1257. var env = process.env.NODE_ENV;
  1258. var write = process.stdout.write;
  1259. var exit = process.exit;
  1260. process.env.NODE_ENV = 'nottatest';
  1261. process.stdout.write = function () { };
  1262. process.exit = function (state) {
  1263. process.exit = exit;
  1264. process.env.NODE_ENV = env;
  1265. process.stdout.write = write;
  1266. expect(state).to.equal(1);
  1267. done();
  1268. };
  1269. Hoek.abort('Boom');
  1270. });
  1271. it('throws when not in test mode and abortThrow is true', function (done) {
  1272. var env = process.env.NODE_ENV;
  1273. process.env.NODE_ENV = 'nottatest';
  1274. Hoek.abortThrow = true;
  1275. var fn = function () {
  1276. Hoek.abort('my error message');
  1277. };
  1278. expect(fn).to.throw('my error message');
  1279. Hoek.abortThrow = false;
  1280. process.env.NODE_ENV = env;
  1281. done();
  1282. });
  1283. it('respects hideStack argument', function (done) {
  1284. var env = process.env.NODE_ENV;
  1285. var write = process.stdout.write;
  1286. var exit = process.exit;
  1287. var output = '';
  1288. process.exit = function () { };
  1289. process.env.NODE_ENV = '';
  1290. process.stdout.write = function (message) {
  1291. output = message;
  1292. };
  1293. Hoek.abort('my error message', true);
  1294. process.env.NODE_ENV = env;
  1295. process.stdout.write = write;
  1296. process.exit = exit;
  1297. expect(output).to.equal('ABORT: my error message\n\t\n');
  1298. done();
  1299. });
  1300. it('throws in test mode', function (done) {
  1301. var env = process.env.NODE_ENV;
  1302. process.env.NODE_ENV = 'test';
  1303. expect(function () {
  1304. Hoek.abort('my error message', true);
  1305. }).to.throw('my error message');
  1306. process.env.NODE_ENV = env;
  1307. done();
  1308. });
  1309. it('throws in test mode with default message', function (done) {
  1310. var env = process.env.NODE_ENV;
  1311. process.env.NODE_ENV = 'test';
  1312. expect(function () {
  1313. Hoek.abort('', true);
  1314. }).to.throw('Unknown error');
  1315. process.env.NODE_ENV = env;
  1316. done();
  1317. });
  1318. it('defaults to showing stack', function (done) {
  1319. var env = process.env.NODE_ENV;
  1320. var write = process.stdout.write;
  1321. var exit = process.exit;
  1322. var output = '';
  1323. process.exit = function () { };
  1324. process.env.NODE_ENV = '';
  1325. process.stdout.write = function (message) {
  1326. output = message;
  1327. };
  1328. Hoek.abort('my error message');
  1329. process.env.NODE_ENV = env;
  1330. process.stdout.write = write;
  1331. process.exit = exit;
  1332. expect(output).to.contain('index.js');
  1333. done();
  1334. });
  1335. });
  1336. describe('assert()', function () {
  1337. it('throws an Error when using assert in a test', function (done) {
  1338. var fn = function () {
  1339. Hoek.assert(false, 'my error message');
  1340. };
  1341. expect(fn).to.throw('my error message');
  1342. done();
  1343. });
  1344. it('throws an Error when using assert in a test with no message', function (done) {
  1345. var fn = function () {
  1346. Hoek.assert(false);
  1347. };
  1348. expect(fn).to.throw('Unknown error');
  1349. done();
  1350. });
  1351. it('throws an Error when using assert in a test with multipart message', function (done) {
  1352. var fn = function () {
  1353. Hoek.assert(false, 'This', 'is', 'my message');
  1354. };
  1355. expect(fn).to.throw('This is my message');
  1356. done();
  1357. });
  1358. it('throws an Error when using assert in a test with multipart message (empty)', function (done) {
  1359. var fn = function () {
  1360. Hoek.assert(false, 'This', 'is', '', 'my message');
  1361. };
  1362. expect(fn).to.throw('This is my message');
  1363. done();
  1364. });
  1365. it('throws an Error when using assert in a test with object message', function (done) {
  1366. var fn = function () {
  1367. Hoek.assert(false, 'This', 'is', { spinal: 'tap' });
  1368. };
  1369. expect(fn).to.throw('This is {"spinal":"tap"}');
  1370. done();
  1371. });
  1372. it('throws an Error when using assert in a test with multipart string and error messages', function (done) {
  1373. var fn = function () {
  1374. Hoek.assert(false, 'This', 'is', new Error('spinal'), new Error('tap'));
  1375. };
  1376. expect(fn).to.throw('This is spinal tap');
  1377. done();
  1378. });
  1379. it('throws an Error when using assert in a test with error object message', function (done) {
  1380. var fn = function () {
  1381. Hoek.assert(false, new Error('This is spinal tap'));
  1382. };
  1383. expect(fn).to.throw('This is spinal tap');
  1384. done();
  1385. });
  1386. it('throws the same Error that is passed to it if there is only one error passed', function (done) {
  1387. var error = new Error('ruh roh');
  1388. var error2 = new Error('ruh roh');
  1389. var fn = function () {
  1390. Hoek.assert(false, error);
  1391. };
  1392. try {
  1393. fn();
  1394. } catch (err) {
  1395. expect(error).to.equal(error); // should be the same reference
  1396. expect(error).to.not.equal(error2); // error with the same message should not match
  1397. }
  1398. done();
  1399. });
  1400. });
  1401. describe('Timer', function () {
  1402. it('returns time elapsed', function (done) {
  1403. var timer = new Hoek.Timer();
  1404. setTimeout(function () {
  1405. expect(timer.elapsed()).to.be.above(9);
  1406. done();
  1407. }, 12);
  1408. });
  1409. });
  1410. describe('Bench', function () {
  1411. it('returns time elapsed', function (done) {
  1412. var timer = new Hoek.Bench();
  1413. setTimeout(function () {
  1414. expect(timer.elapsed()).to.be.above(9);
  1415. done();
  1416. }, 12);
  1417. });
  1418. });
  1419. describe('escapeRegex()', function () {
  1420. it('escapes all special regular expression characters', function (done) {
  1421. var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,');
  1422. expect(a).to.equal('4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`"\\(>\\)\\[<\\]d\\{\\}s\\,');
  1423. done();
  1424. });
  1425. });
  1426. describe('Base64Url', function () {
  1427. var base64str = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w';
  1428. var str = unescape('%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20%21%22%23%24%25%26%27%28%29*+%2C-./0123456789%3A%3B%3C%3D%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF');
  1429. describe('base64urlEncode()', function () {
  1430. it('should base64 URL-safe a string', function (done) {
  1431. expect(Hoek.base64urlEncode(str)).to.equal(base64str);
  1432. done();
  1433. });
  1434. it('encodes a buffer', function (done) {
  1435. expect(Hoek.base64urlEncode(new Buffer(str, 'binary'))).to.equal(base64str);
  1436. done();
  1437. });
  1438. it('should base64 URL-safe a hex string', function (done) {
  1439. var buffer = new Buffer(str, 'binary');
  1440. expect(Hoek.base64urlEncode(buffer.toString('hex'), 'hex')).to.equal(base64str);
  1441. done();
  1442. });
  1443. it('works on larger input strings', function (done) {
  1444. var input = Fs.readFileSync(Path.join(__dirname, 'index.js')).toString();
  1445. var encoded = Hoek.base64urlEncode(input);
  1446. expect(encoded).to.not.contain('+');
  1447. expect(encoded).to.not.contain('/');
  1448. var decoded = Hoek.base64urlDecode(encoded);
  1449. expect(decoded).to.equal(input);
  1450. done();
  1451. });
  1452. });
  1453. describe('base64urlDecode()', function () {
  1454. it('should un-base64 URL-safe a string', function (done) {
  1455. expect(Hoek.base64urlDecode(base64str)).to.equal(str);
  1456. done();
  1457. });
  1458. it('should un-base64 URL-safe a string into hex', function (done) {
  1459. expect(Hoek.base64urlDecode(base64str, 'hex')).to.equal(new Buffer(str, 'binary').toString('hex'));
  1460. done();
  1461. });
  1462. it('should un-base64 URL-safe a string and return a buffer', function (done) {
  1463. var buf = Hoek.base64urlDecode(base64str, 'buffer');
  1464. expect(buf instanceof Buffer).to.equal(true);
  1465. expect(buf.toString('binary')).to.equal(str);
  1466. done();
  1467. });
  1468. it('returns error on undefined input', function (done) {
  1469. expect(Hoek.base64urlDecode().message).to.exist();
  1470. done();
  1471. });
  1472. it('returns error on invalid input', function (done) {
  1473. expect(Hoek.base64urlDecode('*').message).to.exist();
  1474. done();
  1475. });
  1476. });
  1477. });
  1478. describe('escapeHeaderAttribute()', function () {
  1479. it('should not alter ascii values', function (done) {
  1480. var a = Hoek.escapeHeaderAttribute('My Value');
  1481. expect(a).to.equal('My Value');
  1482. done();
  1483. });
  1484. it('escapes all special HTTP header attribute characters', function (done) {
  1485. var a = Hoek.escapeHeaderAttribute('I said go!!!#"' + String.fromCharCode(92));
  1486. expect(a).to.equal('I said go!!!#\\"\\\\');
  1487. done();
  1488. });
  1489. it('throws on large unicode characters', function (done) {
  1490. var fn = function () {
  1491. Hoek.escapeHeaderAttribute('this is a test' + String.fromCharCode(500) + String.fromCharCode(300));
  1492. };
  1493. expect(fn).to.throw(Error);
  1494. done();
  1495. });
  1496. it('throws on CRLF to prevent response splitting', function (done) {
  1497. var fn = function () {
  1498. Hoek.escapeHeaderAttribute('this is a test\r\n');
  1499. };
  1500. expect(fn).to.throw(Error);
  1501. done();
  1502. });
  1503. });
  1504. describe('escapeHtml()', function () {
  1505. it('escapes all special HTML characters', function (done) {
  1506. var a = Hoek.escapeHtml('&<>"\'`');
  1507. expect(a).to.equal('&amp;&lt;&gt;&quot;&#x27;&#x60;');
  1508. done();
  1509. });
  1510. it('returns empty string on falsy input', function (done) {
  1511. var a = Hoek.escapeHtml('');
  1512. expect(a).to.equal('');
  1513. done();
  1514. });
  1515. it('returns unchanged string on no reserved input', function (done) {
  1516. var a = Hoek.escapeHtml('abc');
  1517. expect(a).to.equal('abc');
  1518. done();
  1519. });
  1520. });
  1521. describe('nextTick()', function () {
  1522. it('calls the provided callback on nextTick', function (done) {
  1523. var a = 0;
  1524. var inc = function (step, next) {
  1525. a += step;
  1526. next();
  1527. };
  1528. var ticked = Hoek.nextTick(inc);
  1529. ticked(5, function () {
  1530. expect(a).to.equal(6);
  1531. done();
  1532. });
  1533. expect(a).to.equal(0);
  1534. inc(1, function () {
  1535. expect(a).to.equal(1);
  1536. });
  1537. });
  1538. });
  1539. describe('once()', function () {
  1540. it('allows function to only execute once', function (done) {
  1541. var gen = 0;
  1542. var add = function (x) {
  1543. gen += x;
  1544. };
  1545. add(5);
  1546. expect(gen).to.equal(5);
  1547. add = Hoek.once(add);
  1548. add(5);
  1549. expect(gen).to.equal(10);
  1550. add(5);
  1551. expect(gen).to.equal(10);
  1552. done();
  1553. });
  1554. it('double once wraps one time', function (done) {
  1555. var method = function () { };
  1556. method = Hoek.once(method);
  1557. method.x = 1;
  1558. method = Hoek.once(method);
  1559. expect(method.x).to.equal(1);
  1560. done();
  1561. });
  1562. });
  1563. describe('isAbsoltePath()', function () {
  1564. it('identifies if path is absolute on Unix without node support', { parallel: false }, function (done) {
  1565. var orig = Path.isAbsolute;
  1566. Path.isAbsolute = undefined;
  1567. expect(Hoek.isAbsolutePath('')).to.equal(false);
  1568. expect(Hoek.isAbsolutePath('a')).to.equal(false);
  1569. expect(Hoek.isAbsolutePath('./a')).to.equal(false);
  1570. expect(Hoek.isAbsolutePath('/a')).to.equal(true);
  1571. expect(Hoek.isAbsolutePath('/')).to.equal(true);
  1572. Path.isAbsolute = orig;
  1573. done();
  1574. });
  1575. it('identifies if path is absolute with fake node support', { parallel: false }, function (done) {
  1576. var orig = Path.isAbsolute;
  1577. Path.isAbsolute = function (path) {
  1578. return path[0] === '/';
  1579. };
  1580. expect(Hoek.isAbsolutePath('', 'linux')).to.equal(false);
  1581. expect(Hoek.isAbsolutePath('a', 'linux')).to.equal(false);
  1582. expect(Hoek.isAbsolutePath('./a', 'linux')).to.equal(false);
  1583. expect(Hoek.isAbsolutePath('/a', 'linux')).to.equal(true);
  1584. expect(Hoek.isAbsolutePath('/', 'linux')).to.equal(true);
  1585. Path.isAbsolute = orig;
  1586. done();
  1587. });
  1588. it('identifies if path is absolute on Windows without node support', { parallel: false }, function (done) {
  1589. var orig = Path.isAbsolute;
  1590. Path.isAbsolute = undefined;
  1591. expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true);
  1592. expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true);
  1593. expect(Hoek.isAbsolutePath('\\\\server\\file', 'win32')).to.equal(true);
  1594. expect(Hoek.isAbsolutePath('C:/Users/', 'win32')).to.equal(true);
  1595. expect(Hoek.isAbsolutePath('C:\\Users\\', 'win32')).to.equal(true);
  1596. expect(Hoek.isAbsolutePath('C:cwd/another', 'win32')).to.equal(false);
  1597. expect(Hoek.isAbsolutePath('C:cwd\\another', 'win32')).to.equal(false);
  1598. expect(Hoek.isAbsolutePath('directory/directory', 'win32')).to.equal(false);
  1599. expect(Hoek.isAbsolutePath('directory\\directory', 'win32')).to.equal(false);
  1600. Path.isAbsolute = orig;
  1601. done();
  1602. });
  1603. });
  1604. describe('isInteger()', function () {
  1605. it('validates integers', function (done) {
  1606. expect(Hoek.isInteger(0)).to.equal(true);
  1607. expect(Hoek.isInteger(1)).to.equal(true);
  1608. expect(Hoek.isInteger(1394035612500)).to.equal(true);
  1609. expect(Hoek.isInteger('0')).to.equal(false);
  1610. expect(Hoek.isInteger(1.0)).to.equal(true);
  1611. expect(Hoek.isInteger(1.1)).to.equal(false);
  1612. done();
  1613. });
  1614. });
  1615. describe('ignore()', function () {
  1616. it('exists', function (done) {
  1617. expect(Hoek.ignore).to.exist();
  1618. expect(typeof Hoek.ignore).to.equal('function');
  1619. done();
  1620. });
  1621. });
  1622. describe('inherits()', function () {
  1623. it('exists', function (done) {
  1624. expect(Hoek.inherits).to.exist();
  1625. expect(typeof Hoek.inherits).to.equal('function');
  1626. done();
  1627. });
  1628. });
  1629. describe('format()', function () {
  1630. it('exists', function (done) {
  1631. expect(Hoek.format).to.exist();
  1632. expect(typeof Hoek.format).to.equal('function');
  1633. done();
  1634. });
  1635. it('is a reference to Util.format', function (done) {
  1636. expect(Hoek.format('hello %s', 'world')).to.equal('hello world');
  1637. done();
  1638. });
  1639. });
  1640. describe('transform()', function () {
  1641. var source = {
  1642. address: {
  1643. one: '123 main street',
  1644. two: 'PO Box 1234'
  1645. },
  1646. zip: {
  1647. code: 3321232,
  1648. province: null
  1649. },
  1650. title: 'Warehouse',
  1651. state: 'CA'
  1652. };
  1653. var sourcesArray = [{
  1654. address: {
  1655. one: '123 main street',
  1656. two: 'PO Box 1234'
  1657. },
  1658. zip: {
  1659. code: 3321232,
  1660. province: null
  1661. },
  1662. title: 'Warehouse',
  1663. state: 'CA'
  1664. }, {
  1665. address: {
  1666. one: '456 market street',
  1667. two: 'PO Box 5678'
  1668. },
  1669. zip: {
  1670. code: 9876,
  1671. province: null
  1672. },
  1673. title: 'Garage',
  1674. state: 'NY'
  1675. }];
  1676. it('transforms an object based on the input object', function (done) {
  1677. var result = Hoek.transform(source, {
  1678. 'person.address.lineOne': 'address.one',
  1679. 'person.address.lineTwo': 'address.two',
  1680. 'title': 'title',
  1681. 'person.address.region': 'state',
  1682. 'person.address.zip': 'zip.code',
  1683. 'person.address.location': 'zip.province'
  1684. });
  1685. expect(result).to.deep.equal({
  1686. person: {
  1687. address: {
  1688. lineOne: '123 main street',
  1689. lineTwo: 'PO Box 1234',
  1690. region: 'CA',
  1691. zip: 3321232,
  1692. location: null
  1693. }
  1694. },
  1695. title: 'Warehouse'
  1696. });
  1697. done();
  1698. });
  1699. it('transforms an array of objects based on the input object', function (done) {
  1700. var result = Hoek.transform(sourcesArray, {
  1701. 'person.address.lineOne': 'address.one',
  1702. 'person.address.lineTwo': 'address.two',
  1703. 'title': 'title',
  1704. 'person.address.region': 'state',
  1705. 'person.address.zip': 'zip.code',
  1706. 'person.address.location': 'zip.province'
  1707. });
  1708. expect(result).to.deep.equal([
  1709. {
  1710. person: {
  1711. address: {
  1712. lineOne: '123 main street',
  1713. lineTwo: 'PO Box 1234',
  1714. region: 'CA',
  1715. zip: 3321232,
  1716. location: null
  1717. }
  1718. },
  1719. title: 'Warehouse'
  1720. },
  1721. {
  1722. person: {
  1723. address: {
  1724. lineOne: '456 market street',
  1725. lineTwo: 'PO Box 5678',
  1726. region: 'NY',
  1727. zip: 9876,
  1728. location: null
  1729. }
  1730. },
  1731. title: 'Garage'
  1732. }
  1733. ]);
  1734. done();
  1735. });
  1736. it('uses the reach options passed into it', function (done) {
  1737. var schema = {
  1738. 'person.address.lineOne': 'address-one',
  1739. 'person.address.lineTwo': 'address-two',
  1740. 'title': 'title',
  1741. 'person.address.region': 'state',
  1742. 'person.prefix': 'person-title',
  1743. 'person.zip': 'zip-code'
  1744. };
  1745. var options = {
  1746. separator: '-',
  1747. default: 'unknown'
  1748. };
  1749. var result = Hoek.transform(source, schema, options);
  1750. expect(result).to.deep.equal({
  1751. person: {
  1752. address: {
  1753. lineOne: '123 main street',
  1754. lineTwo: 'PO Box 1234',
  1755. region: 'CA'
  1756. },
  1757. prefix: 'unknown',
  1758. zip: 3321232
  1759. },
  1760. title: 'Warehouse'
  1761. });
  1762. done();
  1763. });
  1764. it('works to create shallow objects', function (done) {
  1765. var result = Hoek.transform(source, {
  1766. lineOne: 'address.one',
  1767. lineTwo: 'address.two',
  1768. title: 'title',
  1769. region: 'state',
  1770. province: 'zip.province'
  1771. });
  1772. expect(result).to.deep.equal({
  1773. lineOne: '123 main street',
  1774. lineTwo: 'PO Box 1234',
  1775. title: 'Warehouse',
  1776. region: 'CA',
  1777. province: null
  1778. });
  1779. done();
  1780. });
  1781. it('only allows strings in the map', function (done) {
  1782. expect(function () {
  1783. var result = Hoek.transform(source, {
  1784. lineOne: {}
  1785. });
  1786. }).to.throw('All mappings must be "." delineated strings');
  1787. done();
  1788. });
  1789. it('throws an error on invalid arguments', function (done) {
  1790. expect(function () {
  1791. var result = Hoek.transform(NaN, {});
  1792. }).to.throw('Invalid source object: must be null, undefined, an object, or an array');
  1793. done();
  1794. });
  1795. it('is safe to pass null', function (done) {
  1796. var result = Hoek.transform(null, {});
  1797. expect(result).to.deep.equal({});
  1798. done();
  1799. });
  1800. it('is safe to pass undefined', function (done) {
  1801. var result = Hoek.transform(undefined, {});
  1802. expect(result).to.deep.equal({});
  1803. done();
  1804. });
  1805. });
  1806. describe('uniqueFilename()', function () {
  1807. it('generates a random file path', function (done) {
  1808. var result = Hoek.uniqueFilename('./test/modules');
  1809. expect(result).to.exist();
  1810. expect(result).to.be.a.string();
  1811. expect(result).to.contain('test/modules');
  1812. done();
  1813. });
  1814. it('is random enough to use in fast loops', function (done) {
  1815. var results = [];
  1816. for (var i = 0; i < 10; ++i) {
  1817. results[i] = Hoek.uniqueFilename('./test/modules');
  1818. }
  1819. var filter = results.filter(function (item, index, array) {
  1820. return array.indexOf(item) === index;
  1821. });
  1822. expect(filter.length).to.equal(10);
  1823. expect(results.length).to.equal(10);
  1824. done();
  1825. });
  1826. it('combines the random elements with a supplied character', function (done) {
  1827. var result = Hoek.uniqueFilename('./test', 'txt');
  1828. expect(result).to.contain('test/');
  1829. expect(result).to.contain('.txt');
  1830. done();
  1831. });
  1832. it('accepts extensions with a "." in it', function (done) {
  1833. var result = Hoek.uniqueFilename('./test', '.mp3');
  1834. expect(result).to.contain('test/');
  1835. expect(result).to.contain('.mp3');
  1836. done();
  1837. });
  1838. });
  1839. describe('stringify()', function (done) {
  1840. it('converts object to string', function (done) {
  1841. var obj = { a: 1 };
  1842. expect(Hoek.stringify(obj)).to.equal('{"a":1}');
  1843. done();
  1844. });
  1845. it('returns error in result string', function (done) {
  1846. var obj = { a: 1 };
  1847. obj.b = obj;
  1848. expect(Hoek.stringify(obj)).to.equal('[Cannot display object: Converting circular structure to JSON]');
  1849. done();
  1850. });
  1851. });
  1852. describe('shallow()', function (done) {
  1853. it('shallow copies an object', function (done) {
  1854. var obj = {
  1855. a: 5,
  1856. b: {
  1857. c: 6
  1858. }
  1859. };
  1860. var shallow = Hoek.shallow(obj);
  1861. expect(shallow).to.not.equal(obj);
  1862. expect(shallow).to.deep.equal(obj);
  1863. expect(shallow.b).to.equal(obj.b);
  1864. done();
  1865. });
  1866. });