Skip to content

Commit d6f2666

Browse files
committedOct 26, 2023
add options to withSpawnMock
1 parent ad379b0 commit d6f2666

File tree

2 files changed

+70
-7
lines changed

2 files changed

+70
-7
lines changed
 

‎src/run-context.ts

+28-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { camelCase, kebabCase, merge as lodashMerge, set as lodashSet } from 'lo
99
import { resetFileCommitStates } from 'mem-fs-editor/state';
1010
import { create as createMemFs, type Store } from 'mem-fs';
1111
import tempDirectory from 'temp-dir';
12-
import { stub as sinonStub } from 'sinon';
12+
import { stub as sinonStub, type SinonStub } from 'sinon';
1313
import type {
1414
BaseEnvironmentOptions,
1515
BaseGenerator,
@@ -407,11 +407,37 @@ export class RunContextBase<GeneratorType extends BaseGenerator = DefaultGenerat
407407
});
408408
}
409409

410-
withSpawnMock(stub = sinonStub()): this {
410+
withSpawnMock(
411+
options?: ((...args) => any) | { stub?: (...args) => any; registerSinonDefaults?: boolean; callback?: (stub) => void | Promise<void> },
412+
): this {
411413
if (this.spawnStub) {
412414
throw new Error('Multiple withSpawnMock calls');
413415
}
414416

417+
const stub = typeof options === 'function' ? options : options?.stub ?? sinonStub();
418+
const registerSinonDefaults = typeof options === 'function' ? false : options?.registerSinonDefaults ?? true;
419+
const callback = typeof options === 'function' ? undefined : options?.callback;
420+
421+
if (registerSinonDefaults) {
422+
// eslint-disable-next-line @typescript-eslint/no-empty-function
423+
const defaultChild = { stdout: { on() {} }, stderr: { on() {} } };
424+
const defaultReturn = { exitCode: 0, stdout: '', stderr: '' };
425+
const stubFn = stub as SinonStub;
426+
427+
// eslint-disable-next-line @typescript-eslint/promise-function-async
428+
stubFn.withArgs('spawnCommand').callsFake(() => Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild));
429+
// eslint-disable-next-line @typescript-eslint/promise-function-async
430+
stubFn.withArgs('spawn').callsFake(() => Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild));
431+
stubFn.withArgs('spawnCommandSync').callsFake(() => ({ ...defaultReturn }));
432+
stubFn.withArgs('spawnSync').callsFake(() => ({ ...defaultReturn }));
433+
}
434+
435+
if (callback) {
436+
this.onBeforePrepare(async () => {
437+
await callback(stub);
438+
});
439+
}
440+
415441
this.spawnStub = stub;
416442
return this.onEnvironment(env => {
417443
env.on('compose', (_namespace, generator) => {

‎test/run-context.spec.ts

+42-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
55
import process from 'node:process';
66
import { createRequire } from 'node:module';
77
import { expect } from 'esmocha';
8-
import { assert as sinonAssert, spy as sinonSpy, stub as sinonStub, fake as sinonFake } from 'sinon';
8+
import { assert as sinonAssert, spy as sinonSpy, stub as sinonStub, fake as sinonFake, type SinonStub } from 'sinon';
99
import inquirer from 'inquirer';
1010
import Generator from 'yeoman-generator';
1111
import tempDirectory from 'temp-dir';
@@ -647,10 +647,17 @@ describe('RunContext', function () {
647647
it('provide arguments to the generator when passed as String', async function () {
648648
ctx.withSpawnMock();
649649
Dummy.prototype.mockTask = async function () {
650-
await this.spawnCommand('foo');
651-
this.spawnCommandSync('foo');
652-
await this.spawn('foo');
653-
this.spawnSync('foo');
650+
const spawnCommandFoo = this.spawnCommand('foo');
651+
expect(spawnCommandFoo).toMatchObject({ stderr: expect.any(Object), stdout: expect.any(Object) });
652+
await expect(spawnCommandFoo).resolves.toMatchObject({ exitCode: 0, stderr: '', stdout: '' });
653+
654+
expect(this.spawnCommandSync('foo')).toMatchObject({ exitCode: 0, stderr: '', stdout: '' });
655+
656+
const spawnFoo = this.spawn('foo');
657+
expect(spawnFoo).toMatchObject({ stderr: expect.any(Object), stdout: expect.any(Object) });
658+
await expect(spawnFoo).resolves.toMatchObject({ exitCode: 0, stderr: '', stdout: '' });
659+
660+
expect(this.spawnSync('foo')).toMatchObject({ exitCode: 0, stderr: '', stdout: '' });
654661
};
655662

656663
const result = await ctx.toPromise();
@@ -659,6 +666,36 @@ describe('RunContext', function () {
659666
assert.deepStrictEqual(result.getSpawnArgsUsingDefaultImplementation()[2], ['spawn', 'foo']);
660667
assert.deepStrictEqual(result.getSpawnArgsUsingDefaultImplementation()[3], ['spawnSync', 'foo']);
661668
});
669+
670+
it('with callback', async function () {
671+
ctx.withSpawnMock({
672+
stub: sinonStub(),
673+
registerSinonDefaults: true,
674+
callback(stub: SinonStub) {
675+
stub.withArgs('spawnCommandSync', 'foo').returns('bar');
676+
},
677+
});
678+
679+
Dummy.prototype.mockTask = async function () {
680+
expect(this.spawnCommandSync()).toMatchObject({ exitCode: 0, stderr: '', stdout: '' });
681+
expect(this.spawnCommandSync('foo')).toBe('bar');
682+
};
683+
684+
await ctx.toPromise();
685+
});
686+
687+
it('without defaults', async function () {
688+
ctx.withSpawnMock({
689+
stub: sinonStub(),
690+
registerSinonDefaults: false,
691+
});
692+
693+
Dummy.prototype.mockTask = async function () {
694+
expect(this.spawnCommandSync()).toBeUndefined();
695+
};
696+
697+
await ctx.toPromise();
698+
});
662699
});
663700

664701
describe('#withEnvironment()', function () {

0 commit comments

Comments
 (0)
Please sign in to comment.