Skip to content

Commit

Permalink
switch to mock from node:test (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
mshima authored Aug 27, 2024
1 parent 9147ad9 commit 8875cd5
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 37 deletions.
22 changes: 17 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
"inquirer": "^9.2.2",
"lodash-es": "^4.17.21",
"mem-fs-editor": "^11.1.1",
"sinon": "^18.0.0",
"temp-dir": "^3.0.0",
"type-fest": "^4.3.1",
"when-exit": "^2.1.3"
Expand All @@ -65,6 +64,7 @@
"lint-staged": "^15.2.9",
"prettier": "^3.3.3",
"prettier-plugin-packagejson": "^2.3.0",
"sinon": "^18.0.0",
"tui-jsdoc-template": "^1.2.2",
"typescript": "~5.2.2",
"yeoman-environment": "^3.18.3",
Expand Down
10 changes: 8 additions & 2 deletions src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { mock } from 'node:test';
import { TestAdapter as BaseTestAdapter, type TestAdapterOptions } from '@yeoman/adapter/testing';
import { spy as sinonSpy, stub as sinonStub } from 'sinon';

export class TestAdapter extends BaseTestAdapter {
constructor(options: TestAdapterOptions = {}) {
super({
spyFactory: ({ returns }) => (returns ? sinonStub().returns(returns) : sinonSpy()),
spyFactory: ({ returns }) =>
returns
? mock.fn(
() => undefined,
() => returns,
)
: mock.fn(),
...options,
});
}
Expand Down
9 changes: 4 additions & 5 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { existsSync, mkdirSync, rmSync } from 'node:fs';
import { resolve } from 'node:path';
import process from 'node:process';
import { mock } from 'node:test';
import { cloneDeep } from 'lodash-es';
import { spy as sinonSpy, stub as sinonStub } from 'sinon';
import type {
BaseEnvironment,
BaseEnvironmentOptions,
Expand All @@ -12,7 +12,6 @@ import type {
InstantiateOptions,
PromptAnswers,
} from '@yeoman/types';
import type { SinonSpiedInstance } from 'sinon';
import type { DefaultEnvironmentApi, DefaultGeneratorApi } from '../types/type-helpers.js';
import { type DummyPromptOptions, TestAdapter, type TestAdapterOptions } from './adapter.js';
import RunContext, { BasicRunContext, type RunContextSettings } from './run-context.js';
Expand Down Expand Up @@ -146,12 +145,12 @@ export class YeomanTest {
/**
* Create a mocked generator
*/
createMockedGenerator(GeneratorClass = GeneratorImplementation): SinonSpiedInstance<DefaultGeneratorApi> {
createMockedGenerator(GeneratorClass = GeneratorImplementation): ReturnType<typeof mock.fn> {
class MockedGenerator extends GeneratorClass {}
const generator = sinonSpy(MockedGenerator);
const generator = mock.fn(MockedGenerator);
for (const methodName of ['run', 'queueTasks', 'runWithOptions', 'queueOwnTasks']) {
Object.defineProperty(MockedGenerator.prototype, methodName, {
value: sinonStub(),
value: mock.fn(),
});
}

Expand Down
40 changes: 25 additions & 15 deletions src/run-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import path, { isAbsolute, join as pathJoin, resolve } from 'node:path';
import assert from 'node:assert';
import { EventEmitter } from 'node:events';
import process from 'node:process';
import { mock } from 'node:test';
import { camelCase, kebabCase, merge as lodashMerge, set as lodashSet } from 'lodash-es';
import { resetFileCommitStates } from 'mem-fs-editor/state';
import { type Store, create as createMemFs } from 'mem-fs';
import tempDirectory from 'temp-dir';
import { type SinonStub, stub as sinonStub } from 'sinon';
import type {
BaseEnvironmentOptions,
BaseGenerator,
Expand Down Expand Up @@ -404,32 +404,42 @@ export class RunContextBase<GeneratorType extends BaseGenerator = DefaultGenerat
});
}

withSpawnMock(
options?: ((...args) => any) | { stub?: (...args) => any; registerSinonDefaults?: boolean; callback?: (stub) => void | Promise<void> },
withSpawnMock<StubType = ReturnType<typeof mock.fn>>(
options?:
| ((...args) => any)
| {
stub?: (...args) => any;
registerNodeMockDefaults?: boolean;
callback?: ({ stub, implementation }: { stub: StubType; implementation: any }) => void | Promise<void>;
},
): this {
if (this.spawnStub) {
throw new Error('Multiple withSpawnMock calls');
}

const stub = typeof options === 'function' ? options : (options?.stub ?? sinonStub());
const registerSinonDefaults = typeof options === 'function' ? false : (options?.registerSinonDefaults ?? true);
const callback = typeof options === 'function' ? undefined : options?.callback;

if (registerSinonDefaults) {
const registerNodeMockDefaults = typeof options === 'function' ? false : (options?.registerNodeMockDefaults ?? true);
let implementation;
if (registerNodeMockDefaults) {
const defaultChild = { stdout: { on() {} }, stderr: { on() {} } };
const defaultReturn = { exitCode: 0, stdout: '', stderr: '' };
const stubFn = stub as SinonStub;

stubFn.withArgs('spawnCommand').callsFake(() => Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild));

stubFn.withArgs('spawn').callsFake(() => Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild));
stubFn.withArgs('spawnCommandSync').callsFake(() => ({ ...defaultReturn }));
stubFn.withArgs('spawnSync').callsFake(() => ({ ...defaultReturn }));
implementation = (...args) => {
const [methodName] = args;
if (methodName === 'spawnCommand' || methodName === 'spawn') {
return Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild);
}
if (methodName === 'spawnCommandSync' || methodName === 'spawnSync') {
return { ...defaultReturn };
}
};
}

const stub = typeof options === 'function' ? options : (options?.stub ?? mock.fn(() => undefined, implementation));
const callback = typeof options === 'function' ? undefined : options?.callback;

if (callback) {
this.onBeforePrepare(async () => {
await callback(stub);
await callback({ stub, implementation });
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/run-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import assert from 'node:assert';
import { existsSync, readFileSync, rmSync } from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { type mock } from 'node:test';
import type { Store } from 'mem-fs';
import { type MemFsEditor, type MemFsEditorFile, create as createMemFsEditor } from 'mem-fs-editor';
import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor } from '@yeoman/types';
Expand Down Expand Up @@ -126,7 +127,7 @@ export default class RunResult<GeneratorType extends BaseGenerator = BaseGenerat
throw new Error('Spawn stub was not found');
}

return this.spawnStub.getCalls().map(call => call.args);
return (this.spawnStub as ReturnType<typeof mock.fn>).mock.calls.map(call => call.arguments);
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/test-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class TestContext {
if (this.autoCleanup) {
this.runContext?.cleanupTemporaryDir();
} else if (this.autoRestore) {

this.runContext?.restore();
}
}
Expand Down
1 change: 0 additions & 1 deletion test/fixtures/generator-simple/composing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import Generator from 'yeoman-generator';

export default class SimpleGenerator extends Generator {
async exec(toCompose) {
console.log(toCompose);
await this.composeWith(toCompose);
}
}
Expand Down
20 changes: 14 additions & 6 deletions test/run-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import assert from 'node:assert';
import { fileURLToPath } from 'node:url';
import process from 'node:process';
import { createRequire } from 'node:module';
import { mock } from 'node:test';
import { expect } from 'esmocha';
import { type SinonStub, assert as sinonAssert, fake as sinonFake, spy as sinonSpy, stub as sinonStub } from 'sinon';
import { assert as sinonAssert, fake as sinonFake, spy as sinonSpy, stub as sinonStub } from 'sinon';
import inquirer from 'inquirer';
import Generator from 'yeoman-generator';
import tempDirectory from 'temp-dir';
Expand Down Expand Up @@ -694,11 +695,18 @@ describe('RunContext', function () {
});

it('with callback', async function () {
ctx.withSpawnMock({
stub: sinonStub(),
registerSinonDefaults: true,
callback(stub: SinonStub) {
stub.withArgs('spawnCommandSync', 'foo').returns('bar');
ctx.withSpawnMock<ReturnType<typeof mock.fn>>({
stub: mock.fn(),
registerNodeMockDefaults: true,
callback({ stub, implementation }) {
const newImplementation = (...args) => {
const [first, second] = args;
if (first === 'spawnCommandSync' && second === 'foo') {
return 'bar';
}
return implementation(...args);
};
stub.mock.mockImplementation(newImplementation);
},
});

Expand Down

0 comments on commit 8875cd5

Please sign in to comment.