1const t = require('tap') 2const { resolve } = require('path') 3const realRunScript = require('@npmcli/run-script') 4const mockNpm = require('../../fixtures/mock-npm') 5const { cleanCwd } = require('../../fixtures/clean-snapshot') 6 7const mockRs = async (t, { windows = false, runScript, ...opts } = {}) => { 8 let RUN_SCRIPTS = [] 9 10 t.afterEach(() => RUN_SCRIPTS = []) 11 12 const mock = await mockNpm(t, { 13 ...opts, 14 command: 'run-script', 15 mocks: { 16 '@npmcli/run-script': Object.assign( 17 async rs => { 18 if (runScript) { 19 await runScript(rs) 20 } 21 RUN_SCRIPTS.push(rs) 22 }, 23 realRunScript 24 ), 25 '{LIB}/utils/is-windows.js': { isWindowsShell: windows }, 26 }, 27 }) 28 29 return { 30 ...mock, 31 RUN_SCRIPTS: () => RUN_SCRIPTS, 32 runScript: mock['run-script'], 33 cleanLogs: () => mock.logs.error.flat().map(v => v.toString()).map(cleanCwd), 34 } 35} 36 37t.test('completion', async t => { 38 const completion = async (t, remain, pkg, isFish = false) => { 39 const { runScript } = await mockRs(t, 40 pkg ? { prefixDir: { 'package.json': JSON.stringify(pkg) } } : {} 41 ) 42 return runScript.completion({ conf: { argv: { remain } }, isFish }) 43 } 44 45 t.test('already have a script name', async t => { 46 const res = await completion(t, ['npm', 'run', 'x']) 47 t.equal(res, undefined) 48 }) 49 t.test('no package.json', async t => { 50 const res = await completion(t, ['npm', 'run']) 51 t.strictSame(res, []) 52 }) 53 t.test('has package.json, no scripts', async t => { 54 const res = await completion(t, ['npm', 'run'], {}) 55 t.strictSame(res, []) 56 }) 57 t.test('has package.json, with scripts', async t => { 58 const res = await completion(t, ['npm', 'run'], { 59 scripts: { hello: 'echo hello', world: 'echo world' }, 60 }) 61 t.strictSame(res, ['hello', 'world']) 62 }) 63 64 t.test('fish shell', async t => { 65 const res = await completion(t, ['npm', 'run'], { 66 scripts: { hello: 'echo hello', world: 'echo world' }, 67 }, true) 68 t.strictSame(res, ['hello\techo hello', 'world\techo world']) 69 }) 70}) 71 72t.test('fail if no package.json', async t => { 73 const { runScript } = await mockRs(t) 74 await t.rejects(runScript.exec([]), { code: 'ENOENT' }) 75 await t.rejects(runScript.exec(['test']), { code: 'ENOENT' }) 76}) 77 78t.test('default env, start, and restart scripts', async t => { 79 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 80 prefixDir: { 81 'package.json': JSON.stringify({ name: 'x', version: '1.2.3' }), 82 'server.js': 'console.log("hello, world")', 83 }, 84 }) 85 86 t.test('start', async t => { 87 await runScript.exec(['start']) 88 t.match(RUN_SCRIPTS(), [ 89 { 90 path: npm.localPrefix, 91 args: [], 92 scriptShell: undefined, 93 stdio: 'inherit', 94 pkg: { name: 'x', version: '1.2.3', _id: 'x@1.2.3', scripts: {} }, 95 event: 'start', 96 }, 97 ]) 98 }) 99 100 t.test('env', async t => { 101 await runScript.exec(['env']) 102 t.match(RUN_SCRIPTS(), [ 103 { 104 path: npm.localPrefix, 105 args: [], 106 scriptShell: undefined, 107 stdio: 'inherit', 108 pkg: { 109 name: 'x', 110 version: '1.2.3', 111 _id: 'x@1.2.3', 112 scripts: { 113 env: 'env', 114 }, 115 }, 116 event: 'env', 117 }, 118 ]) 119 }) 120 121 t.test('restart', async t => { 122 await runScript.exec(['restart']) 123 124 t.match(RUN_SCRIPTS(), [ 125 { 126 path: npm.localPrefix, 127 args: [], 128 scriptShell: undefined, 129 stdio: 'inherit', 130 pkg: { 131 name: 'x', 132 version: '1.2.3', 133 _id: 'x@1.2.3', 134 scripts: { 135 restart: 'npm stop --if-present && npm start', 136 }, 137 }, 138 event: 'restart', 139 }, 140 ]) 141 }) 142}) 143 144t.test('default windows env', async t => { 145 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 146 windows: true, 147 prefixDir: { 148 'package.json': JSON.stringify({ name: 'x', version: '1.2.3' }), 149 'server.js': 'console.log("hello, world")', 150 }, 151 }) 152 await runScript.exec(['env']) 153 t.match(RUN_SCRIPTS(), [ 154 { 155 path: npm.localPrefix, 156 args: [], 157 scriptShell: undefined, 158 stdio: 'inherit', 159 pkg: { 160 name: 'x', 161 version: '1.2.3', 162 _id: 'x@1.2.3', 163 scripts: { 164 env: 'SET', 165 }, 166 }, 167 event: 'env', 168 }, 169 ]) 170}) 171 172t.test('non-default env script', async t => { 173 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 174 prefixDir: { 175 'package.json': JSON.stringify({ 176 name: 'x', 177 version: '1.2.3', 178 scripts: { 179 env: 'hello', 180 }, 181 }), 182 }, 183 }) 184 185 t.test('env', async t => { 186 await runScript.exec(['env']) 187 t.match(RUN_SCRIPTS(), [ 188 { 189 path: npm.localPrefix, 190 args: [], 191 scriptShell: undefined, 192 stdio: 'inherit', 193 pkg: { 194 name: 'x', 195 version: '1.2.3', 196 _id: 'x@1.2.3', 197 scripts: { 198 env: 'hello', 199 }, 200 }, 201 event: 'env', 202 }, 203 ]) 204 }) 205}) 206 207t.test('non-default env script windows', async t => { 208 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 209 windows: true, 210 prefixDir: { 211 'package.json': JSON.stringify({ 212 name: 'x', 213 version: '1.2.3', 214 scripts: { 215 env: 'hello', 216 }, 217 }), 218 }, 219 }) 220 221 await runScript.exec(['env']) 222 223 t.match(RUN_SCRIPTS(), [ 224 { 225 path: npm.localPrefix, 226 args: [], 227 scriptShell: undefined, 228 stdio: 'inherit', 229 pkg: { 230 name: 'x', 231 version: '1.2.3', 232 _id: 'x@1.2.3', 233 scripts: { 234 env: 'hello', 235 }, 236 }, 237 event: 'env', 238 }, 239 ]) 240}) 241 242t.test('try to run missing script', async t => { 243 t.test('errors', async t => { 244 const { runScript } = await mockRs(t, { 245 prefixDir: { 246 'package.json': JSON.stringify({ 247 scripts: { hello: 'world' }, 248 bin: { goodnight: 'moon' }, 249 }), 250 }, 251 }) 252 t.test('no suggestions', async t => { 253 await t.rejects(runScript.exec(['notevenclose']), 'Missing script: "notevenclose"') 254 }) 255 t.test('script suggestions', async t => { 256 await t.rejects(runScript.exec(['helo']), /Missing script: "helo"/) 257 await t.rejects(runScript.exec(['helo']), /npm run hello/) 258 }) 259 t.test('bin suggestions', async t => { 260 await t.rejects(runScript.exec(['goodneght']), /Missing script: "goodneght"/) 261 await t.rejects(runScript.exec(['goodneght']), /npm exec goodnight/) 262 }) 263 }) 264 265 t.test('with --if-present', async t => { 266 const { runScript, RUN_SCRIPTS } = await mockRs(t, { 267 config: { 'if-present': true }, 268 prefixDir: { 269 'package.json': JSON.stringify({ 270 scripts: { hello: 'world' }, 271 bin: { goodnight: 'moon' }, 272 }), 273 }, 274 }) 275 await runScript.exec(['goodbye']) 276 t.strictSame(RUN_SCRIPTS(), [], 'did not try to run anything') 277 }) 278}) 279 280t.test('run pre/post hooks', async t => { 281 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 282 prefixDir: { 283 'package.json': JSON.stringify({ 284 name: 'x', 285 version: '1.2.3', 286 scripts: { 287 preenv: 'echo before the env', 288 postenv: 'echo after the env', 289 }, 290 }), 291 }, 292 }) 293 294 await runScript.exec(['env']) 295 296 t.match(RUN_SCRIPTS(), [ 297 { event: 'preenv' }, 298 { 299 path: npm.localPrefix, 300 args: [], 301 scriptShell: undefined, 302 stdio: 'inherit', 303 pkg: { 304 name: 'x', 305 version: '1.2.3', 306 _id: 'x@1.2.3', 307 scripts: { 308 env: 'env', 309 }, 310 }, 311 event: 'env', 312 }, 313 { event: 'postenv' }, 314 ]) 315}) 316 317t.test('skip pre/post hooks when using ignoreScripts', async t => { 318 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 319 prefixDir: { 320 'package.json': JSON.stringify({ 321 name: 'x', 322 version: '1.2.3', 323 scripts: { 324 preenv: 'echo before the env', 325 postenv: 'echo after the env', 326 }, 327 }), 328 }, 329 config: { 'ignore-scripts': true }, 330 }) 331 332 await runScript.exec(['env']) 333 334 t.same(RUN_SCRIPTS(), [ 335 { 336 path: npm.localPrefix, 337 args: [], 338 scriptShell: undefined, 339 stdio: 'inherit', 340 pkg: { 341 name: 'x', 342 version: '1.2.3', 343 _id: 'x@1.2.3', 344 scripts: { 345 preenv: 'echo before the env', 346 postenv: 'echo after the env', 347 env: 'env', 348 }, 349 }, 350 banner: true, 351 event: 'env', 352 }, 353 ]) 354}) 355 356t.test('run silent', async t => { 357 const { npm, runScript, RUN_SCRIPTS } = await mockRs(t, { 358 prefixDir: { 359 'package.json': JSON.stringify({ 360 name: 'x', 361 version: '1.2.3', 362 scripts: { 363 preenv: 'echo before the env', 364 postenv: 'echo after the env', 365 }, 366 }), 367 }, 368 config: { silent: true }, 369 }) 370 371 await runScript.exec(['env']) 372 t.match(RUN_SCRIPTS(), [ 373 { 374 event: 'preenv', 375 stdio: 'inherit', 376 }, 377 { 378 path: npm.localPrefix, 379 args: [], 380 scriptShell: undefined, 381 stdio: 'inherit', 382 pkg: { 383 name: 'x', 384 version: '1.2.3', 385 _id: 'x@1.2.3', 386 scripts: { 387 env: 'env', 388 }, 389 }, 390 event: 'env', 391 banner: false, 392 }, 393 { 394 event: 'postenv', 395 stdio: 'inherit', 396 }, 397 ]) 398}) 399 400t.test('list scripts', async t => { 401 const scripts = { 402 test: 'exit 2', 403 start: 'node server.js', 404 stop: 'node kill-server.js', 405 preenv: 'echo before the env', 406 postenv: 'echo after the env', 407 } 408 409 const mockList = async (t, config = {}) => { 410 const mock = await mockRs(t, { 411 prefixDir: { 412 'package.json': JSON.stringify({ 413 name: 'x', 414 version: '1.2.3', 415 scripts, 416 }), 417 }, 418 config, 419 }) 420 421 await mock.runScript.exec([]) 422 423 return mock.outputs 424 } 425 426 t.test('no args', async t => { 427 const output = await mockList(t) 428 t.strictSame( 429 output, 430 [ 431 ['Lifecycle scripts included in x@1.2.3:'], 432 [' test\n exit 2'], 433 [' start\n node server.js'], 434 [' stop\n node kill-server.js'], 435 ['\navailable via `npm run-script`:'], 436 [' preenv\n echo before the env'], 437 [' postenv\n echo after the env'], 438 [''], 439 ], 440 'basic report' 441 ) 442 }) 443 444 t.test('silent', async t => { 445 const outputs = await mockList(t, { silent: true }) 446 t.strictSame(outputs, []) 447 }) 448 t.test('warn json', async t => { 449 const outputs = await mockList(t, { json: true }) 450 t.strictSame(outputs, [[JSON.stringify(scripts, 0, 2)]], 'json report') 451 }) 452 453 t.test('parseable', async t => { 454 const outputs = await mockList(t, { parseable: true }) 455 t.strictSame(outputs, [ 456 ['test:exit 2'], 457 ['start:node server.js'], 458 ['stop:node kill-server.js'], 459 ['preenv:echo before the env'], 460 ['postenv:echo after the env'], 461 ]) 462 }) 463}) 464 465t.test('list scripts when no scripts', async t => { 466 const { runScript, outputs } = await mockRs(t, { 467 prefixDir: { 468 'package.json': JSON.stringify({ 469 name: 'x', 470 version: '1.2.3', 471 }), 472 }, 473 }) 474 475 await runScript.exec([]) 476 t.strictSame(outputs, [], 'nothing to report') 477}) 478 479t.test('list scripts, only commands', async t => { 480 const { runScript, outputs } = await mockRs(t, { 481 prefixDir: { 482 'package.json': JSON.stringify({ 483 name: 'x', 484 version: '1.2.3', 485 scripts: { preversion: 'echo doing the version dance' }, 486 }), 487 }, 488 }) 489 490 await runScript.exec([]) 491 t.strictSame(outputs, [ 492 ['Lifecycle scripts included in x@1.2.3:'], 493 [' preversion\n echo doing the version dance'], 494 [''], 495 ]) 496}) 497 498t.test('list scripts, only non-commands', async t => { 499 const { runScript, outputs } = await mockRs(t, { 500 prefixDir: { 501 'package.json': JSON.stringify({ 502 name: 'x', 503 version: '1.2.3', 504 scripts: { glorp: 'echo doing the glerp glop' }, 505 }), 506 }, 507 }) 508 509 await runScript.exec([]) 510 t.strictSame(outputs, [ 511 ['Scripts available in x@1.2.3 via `npm run-script`:'], 512 [' glorp\n echo doing the glerp glop'], 513 [''], 514 ]) 515}) 516 517t.test('workspaces', async t => { 518 const mockWorkspaces = async (t, { 519 runScript, 520 prefixDir, 521 workspaces = true, 522 exec = [], 523 ...config 524 } = {}) => { 525 const mock = await mockRs(t, { 526 prefixDir: prefixDir || { 527 packages: { 528 a: { 529 'package.json': JSON.stringify({ 530 name: 'a', 531 version: '1.0.0', 532 scripts: { glorp: 'echo a doing the glerp glop' }, 533 }), 534 }, 535 b: { 536 'package.json': JSON.stringify({ 537 name: 'b', 538 version: '2.0.0', 539 scripts: { glorp: 'echo b doing the glerp glop' }, 540 }), 541 }, 542 c: { 543 'package.json': JSON.stringify({ 544 name: 'c', 545 version: '1.0.0', 546 scripts: { 547 test: 'exit 0', 548 posttest: 'echo posttest', 549 lorem: 'echo c lorem', 550 }, 551 }), 552 }, 553 d: { 554 'package.json': JSON.stringify({ 555 name: 'd', 556 version: '1.0.0', 557 scripts: { 558 test: 'exit 0', 559 posttest: 'echo posttest', 560 }, 561 }), 562 }, 563 e: { 564 'package.json': JSON.stringify({ 565 name: 'e', 566 scripts: { test: 'exit 0', start: 'echo start something' }, 567 }), 568 }, 569 noscripts: { 570 'package.json': JSON.stringify({ 571 name: 'noscripts', 572 version: '1.0.0', 573 }), 574 }, 575 }, 576 'package.json': JSON.stringify({ 577 name: 'x', 578 version: '1.2.3', 579 workspaces: ['packages/*'], 580 }), 581 }, 582 config: { 583 ...Array.isArray(workspaces) ? { workspace: workspaces } : { workspaces }, 584 ...config, 585 }, 586 runScript, 587 }) 588 if (exec) { 589 await mock.runScript.exec(exec) 590 } 591 return mock 592 } 593 594 t.test('list all scripts', async t => { 595 const { outputs } = await mockWorkspaces(t) 596 t.strictSame(outputs, [ 597 ['Scripts available in a@1.0.0 via `npm run-script`:'], 598 [' glorp\n echo a doing the glerp glop'], 599 [''], 600 ['Scripts available in b@2.0.0 via `npm run-script`:'], 601 [' glorp\n echo b doing the glerp glop'], 602 [''], 603 ['Lifecycle scripts included in c@1.0.0:'], 604 [' test\n exit 0'], 605 [' posttest\n echo posttest'], 606 ['\navailable via `npm run-script`:'], 607 [' lorem\n echo c lorem'], 608 [''], 609 ['Lifecycle scripts included in d@1.0.0:'], 610 [' test\n exit 0'], 611 [' posttest\n echo posttest'], 612 [''], 613 ['Lifecycle scripts included in e:'], 614 [' test\n exit 0'], 615 [' start\n echo start something'], 616 [''], 617 ]) 618 }) 619 620 t.test('list regular scripts, filtered by name', async t => { 621 const { outputs } = await mockWorkspaces(t, { workspaces: ['a', 'b'] }) 622 t.strictSame(outputs, [ 623 ['Scripts available in a@1.0.0 via `npm run-script`:'], 624 [' glorp\n echo a doing the glerp glop'], 625 [''], 626 ['Scripts available in b@2.0.0 via `npm run-script`:'], 627 [' glorp\n echo b doing the glerp glop'], 628 [''], 629 ]) 630 }) 631 632 t.test('list regular scripts, filtered by path', async t => { 633 const { outputs } = await mockWorkspaces(t, { workspaces: ['./packages/a'] }) 634 t.strictSame(outputs, [ 635 ['Scripts available in a@1.0.0 via `npm run-script`:'], 636 [' glorp\n echo a doing the glerp glop'], 637 [''], 638 ]) 639 }) 640 641 t.test('list regular scripts, filtered by parent folder', async t => { 642 const { outputs } = await mockWorkspaces(t, { workspaces: ['./packages'] }) 643 t.strictSame(outputs, [ 644 ['Scripts available in a@1.0.0 via `npm run-script`:'], 645 [' glorp\n echo a doing the glerp glop'], 646 [''], 647 ['Scripts available in b@2.0.0 via `npm run-script`:'], 648 [' glorp\n echo b doing the glerp glop'], 649 [''], 650 ['Lifecycle scripts included in c@1.0.0:'], 651 [' test\n exit 0'], 652 [' posttest\n echo posttest'], 653 ['\navailable via `npm run-script`:'], 654 [' lorem\n echo c lorem'], 655 [''], 656 ['Lifecycle scripts included in d@1.0.0:'], 657 [' test\n exit 0'], 658 [' posttest\n echo posttest'], 659 [''], 660 ['Lifecycle scripts included in e:'], 661 [' test\n exit 0'], 662 [' start\n echo start something'], 663 [''], 664 ]) 665 }) 666 667 t.test('list all scripts with colors', async t => { 668 const { outputs } = await mockWorkspaces(t, { color: 'always' }) 669 t.strictSame(outputs, [ 670 [ 671 /* eslint-disable-next-line max-len */ 672 '\u001b[1mScripts\u001b[22m available in \x1B[32ma@1.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:', 673 ], 674 [' glorp\n \x1B[2mecho a doing the glerp glop\x1B[22m'], 675 [''], 676 [ 677 /* eslint-disable-next-line max-len */ 678 '\u001b[1mScripts\u001b[22m available in \x1B[32mb@2.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:', 679 ], 680 [' glorp\n \x1B[2mecho b doing the glerp glop\x1B[22m'], 681 [''], 682 ['\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32mc@1.0.0\x1B[39m:'], 683 [' test\n \x1B[2mexit 0\x1B[22m'], 684 [' posttest\n \x1B[2mecho posttest\x1B[22m'], 685 ['\navailable via `\x1B[34mnpm run-script\x1B[39m`:'], 686 [' lorem\n \x1B[2mecho c lorem\x1B[22m'], 687 [''], 688 ['\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32md@1.0.0\x1B[39m:'], 689 [' test\n \x1B[2mexit 0\x1B[22m'], 690 [' posttest\n \x1B[2mecho posttest\x1B[22m'], 691 [''], 692 ['\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32me\x1B[39m:'], 693 [' test\n \x1B[2mexit 0\x1B[22m'], 694 [' start\n \x1B[2mecho start something\x1B[22m'], 695 [''], 696 ]) 697 }) 698 699 t.test('list all scripts --json', async t => { 700 const { outputs } = await mockWorkspaces(t, { json: true }) 701 t.strictSame(outputs, [ 702 [ 703 '{\n' + 704 ' "a": {\n' + 705 ' "glorp": "echo a doing the glerp glop"\n' + 706 ' },\n' + 707 ' "b": {\n' + 708 ' "glorp": "echo b doing the glerp glop"\n' + 709 ' },\n' + 710 ' "c": {\n' + 711 ' "test": "exit 0",\n' + 712 ' "posttest": "echo posttest",\n' + 713 ' "lorem": "echo c lorem"\n' + 714 ' },\n' + 715 ' "d": {\n' + 716 ' "test": "exit 0",\n' + 717 ' "posttest": "echo posttest"\n' + 718 ' },\n' + 719 ' "e": {\n' + 720 ' "test": "exit 0",\n' + 721 ' "start": "echo start something"\n' + 722 ' },\n' + 723 ' "noscripts": {}\n' + 724 '}', 725 ], 726 ]) 727 }) 728 729 t.test('list all scripts --parseable', async t => { 730 const { outputs } = await mockWorkspaces(t, { parseable: true }) 731 t.strictSame(outputs, [ 732 ['a:glorp:echo a doing the glerp glop'], 733 ['b:glorp:echo b doing the glerp glop'], 734 ['c:test:exit 0'], 735 ['c:posttest:echo posttest'], 736 ['c:lorem:echo c lorem'], 737 ['d:test:exit 0'], 738 ['d:posttest:echo posttest'], 739 ['e:test:exit 0'], 740 ['e:start:echo start something'], 741 ]) 742 }) 743 744 t.test('list no scripts --loglevel=silent', async t => { 745 const { outputs } = await mockWorkspaces(t, { silent: true }) 746 t.strictSame(outputs, []) 747 }) 748 749 t.test('run scripts across all workspaces', async t => { 750 const { npm, RUN_SCRIPTS } = await mockWorkspaces(t, { exec: ['test'] }) 751 752 t.match(RUN_SCRIPTS(), [ 753 { 754 path: resolve(npm.localPrefix, 'packages/c'), 755 pkg: { name: 'c', version: '1.0.0' }, 756 event: 'test', 757 }, 758 { 759 path: resolve(npm.localPrefix, 'packages/c'), 760 pkg: { name: 'c', version: '1.0.0' }, 761 event: 'posttest', 762 }, 763 { 764 path: resolve(npm.localPrefix, 'packages/d'), 765 pkg: { name: 'd', version: '1.0.0' }, 766 event: 'test', 767 }, 768 { 769 path: resolve(npm.localPrefix, 'packages/d'), 770 pkg: { name: 'd', version: '1.0.0' }, 771 event: 'posttest', 772 }, 773 { 774 path: resolve(npm.localPrefix, 'packages/e'), 775 pkg: { name: 'e' }, 776 event: 'test', 777 }, 778 ]) 779 }) 780 781 t.test('missing scripts in all workspaces', async t => { 782 const { runScript, RUN_SCRIPTS, cleanLogs } = await mockWorkspaces(t, { exec: null }) 783 784 await runScript.exec(['missing-script']) 785 t.match(RUN_SCRIPTS(), []) 786 t.strictSame( 787 cleanLogs(), 788 [ 789 'Lifecycle script `missing-script` failed with error:', 790 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', 791 ' in workspace: a@1.0.0', 792 ' at location: {CWD}/prefix/packages/a', 793 'Lifecycle script `missing-script` failed with error:', 794 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', 795 ' in workspace: b@2.0.0', 796 ' at location: {CWD}/prefix/packages/b', 797 'Lifecycle script `missing-script` failed with error:', 798 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', 799 ' in workspace: c@1.0.0', 800 ' at location: {CWD}/prefix/packages/c', 801 'Lifecycle script `missing-script` failed with error:', 802 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', 803 ' in workspace: d@1.0.0', 804 ' at location: {CWD}/prefix/packages/d', 805 'Lifecycle script `missing-script` failed with error:', 806 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', 807 ' in workspace: e', 808 ' at location: {CWD}/prefix/packages/e', 809 'Lifecycle script `missing-script` failed with error:', 810 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', 811 ' in workspace: noscripts@1.0.0', 812 ' at location: {CWD}/prefix/packages/noscripts', 813 ], 814 'should log error msgs for each workspace script' 815 ) 816 }) 817 818 t.test('missing scripts in some workspaces', async t => { 819 const { RUN_SCRIPTS, cleanLogs } = await mockWorkspaces(t, { 820 exec: ['test'], 821 workspaces: ['a', 'b', 'c', 'd'], 822 }) 823 824 t.match(RUN_SCRIPTS(), []) 825 t.strictSame( 826 cleanLogs(), 827 [ 828 'Lifecycle script `test` failed with error:', 829 'Error: Missing script: "test"\n\nTo see a list of scripts, run:\n npm run', 830 ' in workspace: a@1.0.0', 831 ' at location: {CWD}/prefix/packages/a', 832 'Lifecycle script `test` failed with error:', 833 'Error: Missing script: "test"\n\nTo see a list of scripts, run:\n npm run', 834 ' in workspace: b@2.0.0', 835 ' at location: {CWD}/prefix/packages/b', 836 ], 837 'should log error msgs for each workspace script' 838 ) 839 }) 840 841 t.test('no workspaces when filtering by user args', async t => { 842 await t.rejects( 843 mockWorkspaces(t, { workspaces: ['foo', 'bar'] }), 844 'No workspaces found:\n --workspace=foo --workspace=bar', 845 'should throw error msg' 846 ) 847 }) 848 849 t.test('no workspaces', async t => { 850 await t.rejects( 851 mockWorkspaces(t, { 852 prefixDir: { 853 'package.json': JSON.stringify({ 854 name: 'foo', 855 version: '1.0.0', 856 }), 857 }, 858 }), 859 /No workspaces found!/, 860 'should throw error msg' 861 ) 862 }) 863 864 t.test('single failed workspace run', async t => { 865 const { cleanLogs } = await mockWorkspaces(t, { 866 runScript: () => { 867 throw new Error('err') 868 }, 869 exec: ['test'], 870 workspaces: ['c'], 871 }) 872 873 t.strictSame( 874 cleanLogs(), 875 [ 876 'Lifecycle script `test` failed with error:', 877 'Error: err', 878 ' in workspace: c@1.0.0', 879 ' at location: {CWD}/prefix/packages/c', 880 ], 881 'should log error msgs for each workspace script' 882 ) 883 }) 884 885 t.test('failed workspace run with succeeded runs', async t => { 886 const { cleanLogs, RUN_SCRIPTS, prefix } = await mockWorkspaces(t, { 887 runScript: (opts) => { 888 if (opts.pkg.name === 'a') { 889 throw new Error('ERR') 890 } 891 }, 892 exec: ['glorp'], 893 workspaces: ['a', 'b'], 894 }) 895 896 t.strictSame( 897 cleanLogs(), 898 [ 899 'Lifecycle script `glorp` failed with error:', 900 'Error: ERR', 901 ' in workspace: a@1.0.0', 902 ' at location: {CWD}/prefix/packages/a', 903 ], 904 'should log error msgs for each workspace script' 905 ) 906 907 t.match(RUN_SCRIPTS(), [ 908 { 909 path: resolve(prefix, 'packages/b'), 910 pkg: { name: 'b', version: '2.0.0' }, 911 event: 'glorp', 912 }, 913 ]) 914 }) 915}) 916