1'use strict' 2 3const { test } = require('tap') 4const { retrieveFunding, getFundingInfo } = require('../../lib/utils/funding') 5 6test('empty tree', (t) => { 7 t.deepEqual( 8 getFundingInfo({}), 9 { 10 name: null, 11 dependencies: {}, 12 length: 0 13 }, 14 'should return empty list' 15 ) 16 t.end() 17}) 18 19test('single item missing funding', (t) => { 20 t.deepEqual( 21 getFundingInfo({ name: 'project', 22 dependencies: { 23 'single-item': { 24 name: 'single-item', 25 version: '1.0.0' 26 } 27 }}), 28 { 29 name: 'project', 30 dependencies: {}, 31 length: 0 32 }, 33 'should return empty list' 34 ) 35 t.end() 36}) 37 38test('funding object missing url', (t) => { 39 t.deepEqual( 40 getFundingInfo({ name: 'project', 41 dependencies: { 42 'single-item': { 43 name: 'single-item', 44 version: '1.0.0', 45 funding: { 46 type: 'Foo' 47 } 48 } 49 }}), 50 { 51 name: 'project', 52 dependencies: {}, 53 length: 0 54 }, 55 'should return empty list' 56 ) 57 t.end() 58}) 59 60test('use path if name is missing', (t) => { 61 t.deepEqual( 62 getFundingInfo({ name: undefined, 63 path: '/tmp/foo', 64 children: { 65 'single-item': { 66 name: 'single-item', 67 version: '1.0.0' 68 } 69 }}), 70 { 71 name: '/tmp/foo', 72 dependencies: {}, 73 length: 0 74 }, 75 'should use path as top level name' 76 ) 77 t.end() 78}) 79 80test('single item tree', (t) => { 81 t.deepEqual( 82 getFundingInfo({ name: 'project', 83 dependencies: { 84 'single-item': { 85 name: 'single-item', 86 version: '1.0.0', 87 funding: { 88 type: 'foo', 89 url: 'http://example.com' 90 } 91 } 92 }}), 93 { 94 name: 'project', 95 dependencies: { 96 'single-item': { 97 version: '1.0.0', 98 funding: { 99 type: 'foo', 100 url: 'http://example.com' 101 } 102 } 103 }, 104 length: 1 105 }, 106 'should return list with a single item' 107 ) 108 t.end() 109}) 110 111test('top-level funding info', (t) => { 112 t.deepEqual( 113 getFundingInfo({ name: 'project', 114 funding: 'http://example.com' 115 }), 116 { 117 name: 'project', 118 funding: { 119 url: 'http://example.com' 120 }, 121 dependencies: {}, 122 length: 0 123 }, 124 'should return top-level item with normalized funding info' 125 ) 126 t.end() 127}) 128 129test('use string shorthand', (t) => { 130 t.deepEqual( 131 getFundingInfo({ name: 'project', 132 dependencies: { 133 'single-item': { 134 name: 'single-item', 135 version: '1.0.0', 136 funding: 'http://example.com' 137 } 138 }}), 139 { 140 name: 'project', 141 dependencies: { 142 'single-item': { 143 version: '1.0.0', 144 funding: { 145 url: 'http://example.com' 146 } 147 } 148 }, 149 length: 1 150 }, 151 'should return item with normalized funding info' 152 ) 153 t.end() 154}) 155 156test('duplicate items along the tree', (t) => { 157 t.deepEqual( 158 getFundingInfo({ name: 'project', 159 version: '2.3.4', 160 dependencies: { 161 'single-item': { 162 name: 'single-item', 163 version: '1.0.0', 164 funding: { 165 type: 'foo', 166 url: 'https://example.com' 167 }, 168 dependencies: { 169 'shared-top-first': { 170 name: 'shared-top-first', 171 version: '1.0.0', 172 funding: { 173 type: 'foo', 174 url: 'https://example.com' 175 } 176 }, 177 'sub-dep': { 178 name: 'sub-dep', 179 version: '1.0.0', 180 funding: { 181 type: 'foo', 182 url: 'https://example.com' 183 }, 184 dependencies: { 185 'shared-nested-first': { 186 name: 'shared-nested-first', 187 version: '1.0.0', 188 funding: { 189 type: 'foo', 190 url: 'https://example.com' 191 }, 192 dependencies: { 193 'shared-top-first': { 194 name: 'shared-top-first', 195 version: '1.0.0', 196 funding: { 197 type: 'foo', 198 url: 'https://example.com' 199 } 200 } 201 } 202 } 203 } 204 }, 205 'shared-nested-first': { 206 name: 'shared-nested-first', 207 version: '1.0.0', 208 funding: { 209 type: 'foo', 210 url: 'https://example.com' 211 } 212 } 213 } 214 } 215 }}), 216 { 217 name: 'project', 218 version: '2.3.4', 219 dependencies: { 220 'single-item': { 221 version: '1.0.0', 222 funding: { 223 type: 'foo', 224 url: 'https://example.com' 225 }, 226 dependencies: { 227 'shared-top-first': { 228 version: '1.0.0', 229 funding: { 230 type: 'foo', 231 url: 'https://example.com' 232 } 233 }, 234 'sub-dep': { 235 version: '1.0.0', 236 funding: { 237 type: 'foo', 238 url: 'https://example.com' 239 } 240 }, 241 'shared-nested-first': { 242 version: '1.0.0', 243 funding: { 244 type: 'foo', 245 url: 'https://example.com' 246 } 247 } 248 } 249 } 250 }, 251 length: 4 252 }, 253 'should return list with a single item' 254 ) 255 t.end() 256}) 257 258test('multi-level nested items tree', (t) => { 259 t.deepEqual( 260 getFundingInfo({ name: 'project', 261 dependencies: { 262 'first-level-dep': { 263 name: 'first-level-dep', 264 version: '1.0.0', 265 funding: { 266 type: 'foo', 267 url: 'https://example.com' 268 }, 269 dependencies: { 270 'sub-dep': { 271 name: 'sub-dep', 272 version: '1.0.0', 273 funding: { 274 type: 'foo', 275 url: 'https://example.com' 276 }, 277 dependencies: { 278 package: { 279 name: 'sub-sub-dep', 280 version: '1.0.0', 281 funding: { 282 type: 'foo', 283 url: 'https://example.com' 284 }, 285 dependencies: {} 286 } 287 } 288 } 289 } 290 } 291 }}), 292 { 293 name: 'project', 294 dependencies: { 295 'first-level-dep': { 296 version: '1.0.0', 297 funding: { 298 type: 'foo', 299 url: 'https://example.com' 300 }, 301 dependencies: { 302 'sub-dep': { 303 version: '1.0.0', 304 funding: { 305 type: 'foo', 306 url: 'https://example.com' 307 }, 308 dependencies: { 309 'sub-sub-dep': { 310 version: '1.0.0', 311 funding: { 312 type: 'foo', 313 url: 'https://example.com' 314 } 315 } 316 } 317 } 318 } 319 } 320 }, 321 length: 3 322 }, 323 'should return list with all items' 324 ) 325 t.end() 326}) 327 328test('missing fund nested items tree', (t) => { 329 t.deepEqual( 330 getFundingInfo({ name: 'project', 331 dependencies: { 332 'first-level-dep': { 333 name: 'first-level-dep', 334 version: '1.0.0', 335 funding: { 336 type: 'foo' 337 }, 338 dependencies: { 339 'sub-dep': { 340 name: 'sub-dep', 341 version: '1.0.0', 342 dependencies: { 343 'sub-sub-dep-01': { 344 name: 'sub-sub-dep-01', 345 version: '1.0.0', 346 funding: { 347 type: 'foo', 348 url: 'https://example.com' 349 }, 350 dependencies: { 351 'non-funding-child': { 352 name: 'non-funding-child', 353 version: '1.0.0', 354 dependencies: { 355 'sub-sub-sub-dep': { 356 name: 'sub-sub-sub-dep', 357 version: '1.0.0', 358 funding: { 359 type: 'foo', 360 url: 'https://example.com' 361 } 362 } 363 } 364 } 365 } 366 }, 367 'sub-sub-dep-02': { 368 name: 'sub-sub-dep-02', 369 version: '1.0.0', 370 funding: { 371 type: 'foo', 372 url: 'https://example.com' 373 }, 374 dependencies: {} 375 }, 376 'sub-sub-dep-03': { 377 name: 'sub-sub-dep-03', 378 version: '1.0.0', 379 funding: { 380 type: 'foo', 381 url: 'git://example.git' 382 }, 383 dependencies: { 384 'sub-sub-sub-dep-03': { 385 name: 'sub-sub-sub-dep-03', 386 version: '1.0.0', 387 dependencies: { 388 'sub-sub-sub-sub-dep': { 389 name: 'sub-sub-sub-sub-dep', 390 version: '1.0.0', 391 funding: { 392 type: 'foo', 393 url: 'http://example.com' 394 } 395 } 396 } 397 } 398 } 399 } 400 } 401 } 402 } 403 } 404 }}), 405 { 406 name: 'project', 407 dependencies: { 408 'sub-sub-dep-01': { 409 version: '1.0.0', 410 funding: { 411 type: 'foo', 412 url: 'https://example.com' 413 }, 414 dependencies: { 415 'sub-sub-sub-dep': { 416 version: '1.0.0', 417 funding: { 418 type: 'foo', 419 url: 'https://example.com' 420 } 421 } 422 } 423 }, 424 'sub-sub-dep-02': { 425 version: '1.0.0', 426 funding: { 427 type: 'foo', 428 url: 'https://example.com' 429 } 430 }, 431 'sub-sub-sub-sub-dep': { 432 version: '1.0.0', 433 funding: { 434 type: 'foo', 435 url: 'http://example.com' 436 } 437 } 438 }, 439 length: 4 440 }, 441 'should return list excluding missing funding items' 442 ) 443 t.end() 444}) 445 446test('countOnly option', (t) => { 447 t.deepEqual( 448 getFundingInfo({ name: 'project', 449 dependencies: { 450 'first-level-dep': { 451 name: 'first-level-dep', 452 version: '1.0.0', 453 funding: { 454 type: 'foo' 455 }, 456 dependencies: { 457 'sub-dep': { 458 name: 'sub-dep', 459 version: '1.0.0', 460 funding: { 461 type: 'foo', 462 url: 'https://example.com' 463 }, 464 dependencies: { 465 'sub-sub-dep': { 466 name: 'sub-sub-dep', 467 version: '1.0.0', 468 funding: { 469 type: 'foo', 470 url: 'https://example.com' 471 } 472 }, 473 dependencies: {} 474 } 475 }, 476 'sub-sub-dep': { 477 name: 'sub-sub-dep', 478 version: '1.0.0', 479 funding: { 480 type: 'foo', 481 url: 'https://example.com' 482 } 483 } 484 } 485 } 486 }}, { countOnly: true }), 487 { 488 length: 2 489 }, 490 'should return only the length property' 491 ) 492 t.end() 493}) 494 495test('handle different versions', (t) => { 496 t.deepEqual( 497 getFundingInfo({ name: 'project', 498 dependencies: { 499 foo: { 500 name: 'foo', 501 version: '1.0.0', 502 funding: { 503 type: 'foo', 504 url: 'https://example.com' 505 }, 506 dependencies: { 507 bar: { 508 name: 'bar', 509 version: '1.0.0', 510 funding: { 511 type: 'foo', 512 url: 'https://example.com' 513 } 514 } 515 } 516 }, 517 lorem: { 518 dependencies: { 519 fooo: { 520 name: 'foo', 521 version: '2.0.0', 522 funding: { 523 type: 'foo', 524 url: 'https://example.com' 525 }, 526 dependencies: { 527 'foo-bar': { 528 name: 'foo-bar', 529 version: '1.0.0', 530 funding: { 531 type: 'foo', 532 url: 'https://example.com' 533 } 534 } 535 } 536 } 537 } 538 } 539 } 540 }, { countOnly: true }), 541 { 542 length: 4 543 }, 544 'should treat different versions as diff packages' 545 ) 546 t.end() 547}) 548 549test('retrieve funding info from valid objects', (t) => { 550 t.deepEqual( 551 retrieveFunding({ 552 url: 'http://example.com', 553 type: 'Foo' 554 }), 555 { 556 url: 'http://example.com', 557 type: 'Foo' 558 }, 559 'should return standard object fields' 560 ) 561 t.deepEqual( 562 retrieveFunding({ 563 extra: 'Foo', 564 url: 'http://example.com', 565 type: 'Foo' 566 }), 567 { 568 extra: 'Foo', 569 url: 'http://example.com', 570 type: 'Foo' 571 }, 572 'should leave untouched extra fields' 573 ) 574 t.deepEqual( 575 retrieveFunding({ 576 url: 'http://example.com' 577 }), 578 { 579 url: 'http://example.com' 580 }, 581 'should accept url-only objects' 582 ) 583 t.end() 584}) 585 586test('retrieve funding info from invalid objects', (t) => { 587 t.deepEqual( 588 retrieveFunding({}), 589 {}, 590 'should passthrough empty objects' 591 ) 592 t.deepEqual( 593 retrieveFunding(), 594 undefined, 595 'should not care about undefined' 596 ) 597 t.deepEqual( 598 retrieveFunding(), 599 null, 600 'should not care about null' 601 ) 602 t.end() 603}) 604 605test('retrieve funding info string shorthand', (t) => { 606 t.deepEqual( 607 retrieveFunding('http://example.com'), 608 { 609 url: 'http://example.com' 610 }, 611 'should accept string shorthand' 612 ) 613 t.end() 614}) 615 616test('retrieve funding info from an array', (t) => { 617 t.deepEqual( 618 retrieveFunding([ 619 'http://example.com', 620 { 621 url: 'http://two.example.com' 622 }, 623 'http://three.example.com', 624 { 625 url: 'http://three.example.com', 626 type: 'dos' 627 }, 628 { 629 url: 'http://three.example.com', 630 type: 'third copy!', 631 extra: 'extra metadata!' 632 } 633 ]), 634 [ 635 { 636 url: 'http://example.com' 637 }, 638 { 639 url: 'http://two.example.com' 640 }, 641 { 642 url: 'http://three.example.com' 643 }, 644 { 645 url: 'http://three.example.com', 646 type: 'dos' 647 }, 648 { 649 url: 'http://three.example.com', 650 type: 'third copy!', 651 extra: 'extra metadata!' 652 } 653 ], 654 'should accept and normalize multiple funding sources' 655 ) 656 t.end() 657}) 658