Skip to content

Commit

Permalink
Recombine into a single class with sync and async methods
Browse files Browse the repository at this point in the history
  • Loading branch information
IanVS committed Aug 31, 2021
1 parent 0eed1a4 commit 193ad83
Show file tree
Hide file tree
Showing 6 changed files with 575 additions and 713 deletions.
10 changes: 6 additions & 4 deletions packages/jest-resolve/src/__mocks__/userResolverAsync.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
import {defaultResolverAsync} from '../defaultResolver';

// todo: can be replaced with jest.MockedFunction
declare const userResolver: jest.MockInstance<
ReturnType<typeof defaultResolverAsync>,
Parameters<typeof defaultResolverAsync>
>;
declare const userResolver: {
async: jest.MockInstance<
ReturnType<typeof defaultResolverAsync>,
Parameters<typeof defaultResolverAsync>
>;
};

export default userResolver;
6 changes: 4 additions & 2 deletions packages/jest-resolve/src/__mocks__/userResolverAsync.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

'use strict';

module.exports = function userResolver(path, options) {
return Promise.resolve('module');
module.exports = {
async: function userResolver(path, options) {
return Promise.resolve('module');
},
};
200 changes: 195 additions & 5 deletions packages/jest-resolve/src/__tests__/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@

import * as path from 'path';
import * as fs from 'graceful-fs';
import {sync as resolveSync} from 'resolve';
import resolveAsync = require('resolve');
import {ModuleMap} from 'jest-haste-map';
import userResolver from '../__mocks__/userResolver';
import defaultResolver from '../defaultResolver';
import userResolverAsync from '../__mocks__/userResolverAsync';
import defaultResolver, {defaultResolverAsync} from '../defaultResolver';
import nodeModulesPaths from '../nodeModulesPaths';
import Resolver from '../resolver';
import type {ResolverConfig} from '../types';

jest.mock('../__mocks__/userResolver');
jest.mock('../__mocks__/userResolverAsync');

// Do not fully mock `resolve` because it is used by Jest. Doing it will crash
// in very strange ways. Instead just spy on the method `sync`.
// in very strange ways. Instead just spy on it and its `sync` method.
jest.mock('resolve', () => {
const originalModule = jest.requireActual('resolve');
return {
Expand All @@ -29,11 +31,15 @@ jest.mock('resolve', () => {
});

const mockResolveSync = <
jest.Mock<ReturnType<typeof resolveSync>, Parameters<typeof resolveSync>>
>resolveSync;
jest.Mock<
ReturnType<typeof resolveAsync.sync>,
Parameters<typeof resolveAsync.sync>
>
>resolveAsync.sync;

beforeEach(() => {
userResolver.mockClear();
userResolverAsync.async.mockClear();
mockResolveSync.mockClear();
});

Expand Down Expand Up @@ -146,6 +152,65 @@ describe('findNodeModule', () => {
});
});

describe('findNodeModuleAsync', () => {
it('is possible to override the default resolver', async () => {
const cwd = process.cwd();
const resolvedCwd = fs.realpathSync(cwd) || cwd;
const nodePaths = process.env.NODE_PATH
? process.env.NODE_PATH.split(path.delimiter)
.filter(Boolean)
.map(p => path.resolve(resolvedCwd, p))
: null;

userResolverAsync.async.mockImplementation(() => Promise.resolve('module'));

const newPath = await Resolver.findNodeModuleAsync('test', {
basedir: '/',
browser: true,
extensions: ['js'],
moduleDirectory: ['node_modules'],
paths: ['/something'],
resolver: require.resolve('../__mocks__/userResolverAsync'),
});

expect(newPath).toBe('module');
expect(userResolverAsync.async.mock.calls[0][0]).toBe('test');
expect(userResolverAsync.async.mock.calls[0][1]).toStrictEqual({
basedir: '/',
browser: true,
defaultResolver,
defaultResolverAsync,
extensions: ['js'],
moduleDirectory: ['node_modules'],
paths: (nodePaths || []).concat(['/something']),
rootDir: undefined,
});
});

it('passes packageFilter to the resolve module when using the default resolver', async () => {
const packageFilter = jest.fn();

// A resolver that delegates to defaultResolver with a packageFilter implementation
userResolverAsync.async.mockImplementation((request, opts) =>
Promise.resolve(opts.defaultResolver(request, {...opts, packageFilter})),
);

await Resolver.findNodeModuleAsync('test', {
basedir: '/',
resolver: require.resolve('../__mocks__/userResolverAsync'),
});

// Question, if we can't mock resolve (async), how do we test this?

// expect(mockResolveSync).toHaveBeenCalledWith(
// 'test',
// expect.objectContaining({
// packageFilter,
// }),
// );
});
});

describe('resolveModule', () => {
let moduleMap: ModuleMap;
beforeEach(() => {
Expand Down Expand Up @@ -236,6 +301,103 @@ describe('resolveModule', () => {
});
});

describe('resolveModuleAsync', () => {
let moduleMap: ModuleMap;
beforeEach(() => {
moduleMap = ModuleMap.create('/');
});

it('is possible to resolve node modules', async () => {
const resolver = new Resolver(moduleMap, {
extensions: ['.js'],
} as ResolverConfig);
const src = require.resolve('../');
const resolved = await resolver.resolveModuleAsync(
src,
'./__mocks__/mockJsDependency',
);
expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js'));
});

it('is possible to resolve node modules with custom extensions', async () => {
const resolver = new Resolver(moduleMap, {
extensions: ['.js', '.jsx'],
} as ResolverConfig);
const src = require.resolve('../');
const resolvedJsx = await resolver.resolveModuleAsync(
src,
'./__mocks__/mockJsxDependency',
);
expect(resolvedJsx).toBe(
require.resolve('../__mocks__/mockJsxDependency.jsx'),
);
});

it('is possible to resolve node modules with custom extensions and platforms', async () => {
const resolver = new Resolver(moduleMap, {
extensions: ['.js', '.jsx'],
platforms: ['native'],
} as ResolverConfig);
const src = require.resolve('../');
const resolvedJsx = await resolver.resolveModuleAsync(
src,
'./__mocks__/mockJsxDependency',
);
expect(resolvedJsx).toBe(
require.resolve('../__mocks__/mockJsxDependency.native.jsx'),
);
});

it('is possible to resolve node modules by resolving their realpath', async () => {
const resolver = new Resolver(moduleMap, {
extensions: ['.js'],
} as ResolverConfig);
const src = path.join(
path.resolve(__dirname, '../../src/__mocks__/bar/node_modules/'),
'foo/index.js',
);
const resolved = await resolver.resolveModuleAsync(src, 'dep');
expect(resolved).toBe(
require.resolve('../../src/__mocks__/foo/node_modules/dep/index.js'),
);
});

it('is possible to specify custom resolve paths', async () => {
const resolver = new Resolver(moduleMap, {
extensions: ['.js'],
} as ResolverConfig);
const src = require.resolve('../');
const resolved = await resolver.resolveModuleAsync(
src,
'mockJsDependency',
{
paths: [
path.resolve(__dirname, '../../src/__tests__'),
path.resolve(__dirname, '../../src/__mocks__'),
],
},
);
expect(resolved).toBe(require.resolve('../__mocks__/mockJsDependency.js'));
});

it('does not confuse directories with files', async () => {
const resolver = new Resolver(moduleMap, {
extensions: ['.js'],
} as ResolverConfig);
const mocksDirectory = path.resolve(__dirname, '../__mocks__');
const fooSlashFoo = path.join(mocksDirectory, 'foo/foo.js');
const fooSlashIndex = path.join(mocksDirectory, 'foo/index.js');

const resolvedWithSlash = await resolver.resolveModuleAsync(
fooSlashFoo,
'./',
);
const resolvedWithDot = await resolver.resolveModuleAsync(fooSlashFoo, '.');
expect(resolvedWithSlash).toBe(fooSlashIndex);
expect(resolvedWithSlash).toBe(resolvedWithDot);
});
});

describe('getMockModule', () => {
it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', () => {
userResolver.mockImplementation(() => 'module');
Expand Down Expand Up @@ -263,6 +425,34 @@ describe('getMockModule', () => {
});
});

describe('getMockModuleAsync', () => {
it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', async () => {
userResolverAsync.async.mockImplementation(() => Promise.resolve('module'));

const moduleMap = ModuleMap.create('/');
const resolver = new Resolver(moduleMap, {
extensions: ['.js'],
moduleNameMapper: [
{
moduleName: '$1',
regex: /(.*)/,
},
],
resolver: require.resolve('../__mocks__/userResolverAsync'),
} as ResolverConfig);
const src = require.resolve('../');

await resolver.resolveModuleAsync(src, 'dependentModule');

expect(userResolverAsync.async).toHaveBeenCalled();
expect(userResolverAsync.async.mock.calls[0][0]).toBe('dependentModule');
expect(userResolverAsync.async.mock.calls[0][1]).toHaveProperty(
'basedir',
path.dirname(src),
);
});
});

describe('nodeModulesPaths', () => {
it('provides custom module paths after node_modules', () => {
const src = require.resolve('../');
Expand Down

0 comments on commit 193ad83

Please sign in to comment.