1const { resolve } = require('path') 2const { readFileSync } = require('fs') 3const t = require('tap') 4const _mockNpm = require('../../fixtures/mock-npm') 5const { cleanCwd } = require('../../fixtures/clean-snapshot') 6 7t.cleanSnapshot = (str) => cleanCwd(str) 8 9const mockNpm = async (t, { ...opts } = {}) => { 10 const res = await _mockNpm(t, opts) 11 12 const readPackageJson = (dir = '') => 13 JSON.parse(readFileSync(resolve(res.prefix, dir, 'package.json'), 'utf8')) 14 15 return { 16 ...res, 17 pkg: (...args) => res.npm.exec('pkg', args), 18 readPackageJson, 19 OUTPUT: () => res.joinedOutput(), 20 } 21} 22 23t.test('no args', async t => { 24 const { pkg } = await mockNpm(t) 25 26 await t.rejects( 27 pkg(), 28 { code: 'EUSAGE' }, 29 'should throw usage error' 30 ) 31}) 32 33t.test('no global mode', async t => { 34 const { pkg } = await mockNpm(t, { 35 config: { global: true }, 36 }) 37 38 await t.rejects( 39 pkg('get', 'foo'), 40 { code: 'EPKGGLOBAL' }, 41 'should throw no global mode error' 42 ) 43}) 44 45t.test('get no args', async t => { 46 const { pkg, OUTPUT } = await mockNpm(t, { 47 prefixDir: { 48 'package.json': JSON.stringify({ 49 name: 'foo', 50 version: '1.1.1', 51 }), 52 }, 53 }) 54 55 await pkg('get') 56 57 t.strictSame( 58 JSON.parse(OUTPUT()), 59 { 60 name: 'foo', 61 version: '1.1.1', 62 }, 63 'should print package.json content' 64 ) 65}) 66 67t.test('get single arg', async t => { 68 const { pkg, OUTPUT } = await mockNpm(t, { 69 prefixDir: { 70 'package.json': JSON.stringify({ 71 name: 'foo', 72 version: '1.1.1', 73 }), 74 }, 75 }) 76 77 await pkg('get', 'version') 78 79 t.strictSame( 80 JSON.parse(OUTPUT()), 81 '1.1.1', 82 'should print retrieved package.json field' 83 ) 84}) 85 86t.test('get nested arg', async t => { 87 const { pkg, OUTPUT } = await mockNpm(t, { 88 prefixDir: { 89 'package.json': JSON.stringify({ 90 name: 'foo', 91 version: '1.1.1', 92 scripts: { 93 test: 'node test.js', 94 }, 95 }), 96 }, 97 }) 98 99 await pkg('get', 'scripts.test') 100 101 t.strictSame( 102 JSON.parse(OUTPUT()), 103 'node test.js', 104 'should print retrieved nested field' 105 ) 106}) 107 108t.test('get array field', async t => { 109 const files = [ 110 'index.js', 111 'cli.js', 112 ] 113 const { pkg, OUTPUT } = await mockNpm(t, { 114 prefixDir: { 115 'package.json': JSON.stringify({ 116 name: 'foo', 117 version: '1.1.1', 118 files, 119 }), 120 }, 121 }) 122 123 await pkg('get', 'files') 124 125 t.strictSame( 126 JSON.parse(OUTPUT()), 127 files, 128 'should print retrieved array field' 129 ) 130}) 131 132t.test('get array item', async t => { 133 const files = [ 134 'index.js', 135 'cli.js', 136 ] 137 const { pkg, OUTPUT } = await mockNpm(t, { 138 prefixDir: { 139 'package.json': JSON.stringify({ 140 name: 'foo', 141 version: '1.1.1', 142 files, 143 }), 144 }, 145 }) 146 147 await pkg('get', 'files[0]') 148 149 t.strictSame( 150 JSON.parse(OUTPUT()), 151 'index.js', 152 'should print retrieved array field' 153 ) 154}) 155 156t.test('get array nested items notation', async t => { 157 const contributors = [ 158 { 159 name: 'Ruy', 160 url: 'http://example.com/ruy', 161 }, 162 { 163 name: 'Gar', 164 url: 'http://example.com/gar', 165 }, 166 ] 167 const { pkg, OUTPUT } = await mockNpm(t, { 168 prefixDir: { 169 'package.json': JSON.stringify({ 170 name: 'foo', 171 version: '1.1.1', 172 contributors, 173 }), 174 }, 175 }) 176 177 await pkg('get', 'contributors.name') 178 t.strictSame( 179 JSON.parse(OUTPUT()), 180 { 181 'contributors[0].name': 'Ruy', 182 'contributors[1].name': 'Gar', 183 }, 184 'should print json result containing matching results' 185 ) 186}) 187 188t.test('set no args', async t => { 189 const { pkg } = await mockNpm(t, { 190 prefixDir: { 191 'package.json': JSON.stringify({ name: 'foo' }), 192 }, 193 }) 194 await t.rejects( 195 pkg('set'), 196 { code: 'EUSAGE' }, 197 'should throw an error if no args' 198 ) 199}) 200 201t.test('set missing value', async t => { 202 const { pkg } = await mockNpm(t, { 203 prefixDir: { 204 'package.json': JSON.stringify({ name: 'foo' }), 205 }, 206 }) 207 await t.rejects( 208 pkg('set', 'key='), 209 { code: 'EUSAGE' }, 210 'should throw an error if missing value' 211 ) 212}) 213 214t.test('set missing key', async t => { 215 const { pkg } = await mockNpm(t, { 216 prefixDir: { 217 'package.json': JSON.stringify({ name: 'foo' }), 218 }, 219 }) 220 await t.rejects( 221 pkg('set', '=value'), 222 { code: 'EUSAGE' }, 223 'should throw an error if missing key' 224 ) 225}) 226 227t.test('set single field', async t => { 228 const json = { 229 name: 'foo', 230 version: '1.1.1', 231 } 232 const { pkg, readPackageJson } = await mockNpm(t, { 233 prefixDir: { 234 'package.json': JSON.stringify(json), 235 }, 236 }) 237 238 await pkg('set', 'description=Awesome stuff') 239 t.strictSame( 240 readPackageJson(), 241 { 242 ...json, 243 description: 'Awesome stuff', 244 }, 245 'should add single field to package.json' 246 ) 247}) 248 249t.test('push to array syntax', async t => { 250 const json = { 251 name: 'foo', 252 version: '1.1.1', 253 keywords: [ 254 'foo', 255 ], 256 } 257 const { pkg, readPackageJson } = await mockNpm(t, { 258 prefixDir: { 259 'package.json': JSON.stringify(json), 260 }, 261 }) 262 263 await pkg('set', 'keywords[]=bar', 'keywords[]=baz') 264 t.strictSame( 265 readPackageJson(), 266 { 267 ...json, 268 keywords: [ 269 'foo', 270 'bar', 271 'baz', 272 ], 273 }, 274 'should append to arrays using empty bracket syntax' 275 ) 276}) 277 278t.test('set multiple fields', async t => { 279 const json = { 280 name: 'foo', 281 version: '1.1.1', 282 } 283 const { pkg, readPackageJson } = await mockNpm(t, { 284 prefixDir: { 285 'package.json': JSON.stringify(json), 286 }, 287 }) 288 289 await pkg('set', 'bin.foo=foo.js', 'scripts.test=node test.js') 290 t.strictSame( 291 readPackageJson(), 292 { 293 ...json, 294 bin: { 295 foo: 'foo.js', 296 }, 297 scripts: { 298 test: 'node test.js', 299 }, 300 }, 301 'should add single field to package.json' 302 ) 303}) 304 305t.test('set = separate value', async t => { 306 const json = { 307 name: 'foo', 308 version: '1.1.1', 309 } 310 const { pkg, readPackageJson } = await mockNpm(t, { 311 prefixDir: { 312 'package.json': JSON.stringify(json), 313 }, 314 }) 315 316 await pkg('set', 'tap[test-env][0]=LC_ALL=sk') 317 t.strictSame( 318 readPackageJson(), 319 { 320 ...json, 321 tap: { 322 'test-env': [ 323 'LC_ALL=sk', 324 ], 325 }, 326 }, 327 'should add single field to package.json' 328 ) 329}) 330 331t.test('set --json', async t => { 332 const { pkg, readPackageJson } = await mockNpm(t, { 333 prefixDir: { 334 'package.json': JSON.stringify({ 335 name: 'foo', 336 version: '1.1.1', 337 }), 338 }, 339 config: { json: true }, 340 }) 341 342 await pkg('set', 'private=true') 343 t.strictSame( 344 readPackageJson(), 345 { 346 name: 'foo', 347 version: '1.1.1', 348 private: true, 349 }, 350 'should add boolean field to package.json' 351 ) 352 353 await pkg('set', 'tap.timeout=60') 354 t.strictSame( 355 readPackageJson(), 356 { 357 name: 'foo', 358 version: '1.1.1', 359 private: true, 360 tap: { 361 timeout: 60, 362 }, 363 }, 364 'should add number field to package.json' 365 ) 366 367 await pkg('set', 'foo={ "bar": { "baz": "BAZ" } }') 368 t.strictSame( 369 readPackageJson(), 370 { 371 name: 'foo', 372 version: '1.1.1', 373 private: true, 374 tap: { 375 timeout: 60, 376 }, 377 foo: { 378 bar: { 379 baz: 'BAZ', 380 }, 381 }, 382 }, 383 'should add object field to package.json' 384 ) 385 386 await pkg('set', 'workspaces=["packages/*"]') 387 t.strictSame( 388 readPackageJson(), 389 { 390 name: 'foo', 391 version: '1.1.1', 392 private: true, 393 workspaces: [ 394 'packages/*', 395 ], 396 tap: { 397 timeout: 60, 398 }, 399 foo: { 400 bar: { 401 baz: 'BAZ', 402 }, 403 }, 404 }, 405 'should add object field to package.json' 406 ) 407 408 await pkg('set', 'description="awesome"') 409 t.strictSame( 410 readPackageJson(), 411 { 412 name: 'foo', 413 version: '1.1.1', 414 description: 'awesome', 415 private: true, 416 workspaces: [ 417 'packages/*', 418 ], 419 tap: { 420 timeout: 60, 421 }, 422 foo: { 423 bar: { 424 baz: 'BAZ', 425 }, 426 }, 427 }, 428 'should add object field to package.json' 429 ) 430}) 431 432t.test('delete no args', async t => { 433 const { pkg } = await mockNpm(t, { 434 prefixDir: { 435 'package.json': JSON.stringify({ name: 'foo' }), 436 }, 437 }) 438 await t.rejects( 439 pkg('delete'), 440 { code: 'EUSAGE' }, 441 'should throw an error if deleting no args' 442 ) 443}) 444 445t.test('delete invalid key', async t => { 446 const { pkg } = await mockNpm(t, { 447 prefixDir: { 448 'package.json': JSON.stringify({ name: 'foo' }), 449 }, 450 }) 451 await t.rejects( 452 pkg('delete', ''), 453 { code: 'EUSAGE' }, 454 'should throw an error if deleting invalid args' 455 ) 456}) 457 458t.test('delete single field', async t => { 459 const { pkg, readPackageJson } = await mockNpm(t, { 460 prefixDir: { 461 'package.json': JSON.stringify({ 462 name: 'foo', 463 version: '1.0.0', 464 }), 465 }, 466 }) 467 await pkg('delete', 'version') 468 t.strictSame( 469 readPackageJson(), 470 { 471 name: 'foo', 472 }, 473 'should delete single field from package.json' 474 ) 475}) 476 477t.test('delete multiple field', async t => { 478 const { pkg, readPackageJson } = await mockNpm(t, { 479 prefixDir: { 480 'package.json': JSON.stringify({ 481 name: 'foo', 482 version: '1.0.0', 483 description: 'awesome', 484 }), 485 }, 486 }) 487 await pkg('delete', 'version', 'description') 488 t.strictSame( 489 readPackageJson(), 490 { 491 name: 'foo', 492 }, 493 'should delete multiple fields from package.json' 494 ) 495}) 496 497t.test('delete nested field', async t => { 498 const { pkg, readPackageJson } = await mockNpm(t, { 499 prefixDir: { 500 'package.json': JSON.stringify({ 501 name: 'foo', 502 version: '1.0.0', 503 info: { 504 foo: { 505 bar: [ 506 { 507 baz: 'deleteme', 508 }, 509 ], 510 }, 511 }, 512 }), 513 }, 514 }) 515 await pkg('delete', 'info.foo.bar[0].baz') 516 t.strictSame( 517 readPackageJson(), 518 { 519 name: 'foo', 520 version: '1.0.0', 521 info: { 522 foo: { 523 bar: [ 524 {}, 525 ], 526 }, 527 }, 528 }, 529 'should delete nested fields from package.json' 530 ) 531}) 532 533t.test('workspaces', async t => { 534 const { pkg, OUTPUT, readPackageJson } = await mockNpm(t, { 535 prefixDir: { 536 'package.json': JSON.stringify({ 537 name: 'root', 538 version: '1.0.0', 539 workspaces: [ 540 'packages/*', 541 ], 542 }), 543 packages: { 544 a: { 545 'package.json': JSON.stringify({ 546 name: 'a', 547 version: '1.0.0', 548 }), 549 }, 550 b: { 551 'package.json': JSON.stringify({ 552 name: 'b', 553 version: '1.2.3', 554 }), 555 }, 556 }, 557 }, 558 config: { workspaces: true }, 559 }) 560 561 await pkg('get', 'name', 'version') 562 563 t.strictSame( 564 JSON.parse(OUTPUT()), 565 { 566 a: { 567 name: 'a', 568 version: '1.0.0', 569 }, 570 b: { 571 name: 'b', 572 version: '1.2.3', 573 }, 574 }, 575 'should return expected result for configured workspaces' 576 ) 577 578 await pkg('set', 'funding=http://example.com') 579 580 t.strictSame( 581 readPackageJson('packages/a'), 582 { 583 name: 'a', 584 version: '1.0.0', 585 funding: 'http://example.com', 586 }, 587 'should add field to workspace a' 588 ) 589 590 t.strictSame( 591 readPackageJson('packages/b'), 592 { 593 name: 'b', 594 version: '1.2.3', 595 funding: 'http://example.com', 596 }, 597 'should add field to workspace b' 598 ) 599 600 await pkg('delete', 'version') 601 602 t.strictSame( 603 readPackageJson('packages/a'), 604 { 605 name: 'a', 606 funding: 'http://example.com', 607 }, 608 'should delete version field from workspace a' 609 ) 610 611 t.strictSame( 612 readPackageJson('packages/b'), 613 { 614 name: 'b', 615 funding: 'http://example.com', 616 }, 617 'should delete version field from workspace b' 618 ) 619}) 620 621t.test('fix', async t => { 622 const { pkg, readPackageJson } = await mockNpm(t, { 623 prefixDir: { 624 'package.json': JSON.stringify({ 625 name: 'foo ', 626 version: 'v1.1.1', 627 }), 628 }, 629 }) 630 631 await pkg('fix') 632 t.strictSame( 633 readPackageJson(), 634 { name: 'foo', version: '1.1.1' }, 635 'fixes package.json issues' 636 ) 637}) 638