parseHookNames Test
parseHookNames Test
// Note that this test uses React components declared in the "__source__"
directory.
// This is done to control if and how the code is transformed at runtime.
// Do not declare test components within this test file as it is very fragile.
function initFetchMock() {
const fetchMock = require('jest-fetch-mock');
fetchMock.enableMocks();
fetchMock.mockIf(/.+$/, request => {
const url = request.url;
const isLoadingExternalSourceMap = /external\/.*\.map/.test(url);
if (isLoadingExternalSourceMap) {
// Assert that url contains correct query params
expect(url.includes('?foo=bar¶m=some_value')).toBe(true);
const fileSystemPath = url.split('?')[0];
return requireText(fileSystemPath, 'utf8');
}
return requireText(url, 'utf8');
});
return fetchMock;
}
describe('parseHookNames', () => {
let fetchMock;
let inspectHooks;
let parseHookNames;
beforeEach(() => {
jest.resetModules();
jest.mock('source-map-support', () => {
console.trace('source-map-support');
});
fetchMock = initFetchMock();
inspectHooks =
require('react-debug-tools/src/ReactDebugHooks').inspectHooks;
afterEach(() => {
fetch.resetMocks();
});
it('should skip loading source files for unnamed hooks like useEffect', async ()
=> {
const Component =
require('./__source__/__untransformed__/ComponentWithUseEffect').Component;
// Since this component contains only unnamed hooks, the source code should not
even be loaded.
fetchMock.mockIf(/.+$/, request => {
throw Error(`Unexpected file request for "${request.url}"`);
});
it('should skip loading source files for unnamed hooks like useEffect
(alternate)', async () => {
const Component =
require('./__source__/__untransformed__/ComponentWithExternalUseEffect').Component;
require('./__source__/__untransformed__/ComponentWithNamedCustomHooks').Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, [
'foo',
null, // Custom hooks can have names, but not when using destructuring.
'baz',
]);
});
it('should parse names for code using hooks indirectly', async () => {
const Component =
require('./__source__/__untransformed__/ComponentUsingHooksIndirectly').Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, ['count', 'darkMode', 'isDarkMode']);
});
it('should parse names for code using nested hooks', async () => {
const Component =
require('./__source__/__untransformed__/ComponentWithNestedHooks').Component;
let InnerComponent;
const hookNames = await getHookNamesForComponent(Component, {
callback: innerComponent => {
InnerComponent = innerComponent;
},
});
const innerHookNames = await getHookNamesForComponent(InnerComponent);
expectHookNamesToEqual(hookNames, ['InnerComponent']);
expectHookNamesToEqual(innerHookNames, ['state']);
});
it('should return null for custom hooks without explicit names', async () => {
const Component =
require('./__source__/__untransformed__/ComponentWithUnnamedCustomHooks').Component
;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, [
null, // Custom hooks can have names, but this one does not even return a
value.
null, // Custom hooks can have names, but not when using destructuring.
null, // Custom hooks can have names, but not when using destructuring.
]);
});
// TODO Test that cached metadata is purged when Fast Refresh scheduled
it('should work with more complex files and components', async () => {
async function test(path, name = undefined) {
const components = name != null ? require(path)[name] : require(path);
await test(
'./__source__/__compiled__/inline/ComponentUsingHooksIndirectly',
); // inline source map
await test(
'./__source__/__compiled__/external/ComponentUsingHooksIndirectly',
); // external source map
await test(
'./__source__/__compiled__/inline/index-map/ComponentUsingHooksIndirectly',
); // inline index map source map
await test(
'./__source__/__compiled__/external/index-map/ComponentUsingHooksIndirectly',
); // external index map source map
await test(
'./__source__/__compiled__/bundle',
'ComponentUsingHooksIndirectly',
); // bundle source map
await test(
'./__source__/__compiled__/no-columns/ComponentUsingHooksIndirectly',
); // simulated Webpack 'cheap-module-source-map'
});
// We can't test the uncompiled source here, because it either needs to get
transformed,
// which would break the source mapping, or the import statements will fail.
await test(
'./__source__/__compiled__/inline/ComponentWithExternalCustomHooks',
); // inline source map
await test(
'./__source__/__compiled__/external/ComponentWithExternalCustomHooks',
); // external source map
await test(
'./__source__/__compiled__/inline/index-map/ComponentWithExternalCustomHooks',
); // inline index map source map
await test(
'./__source__/__compiled__/external/index-map/ComponentWithExternalCustomHooks',
); // external index map source map
await test(
'./__source__/__compiled__/bundle',
'ComponentWithExternalCustomHooks',
); // bundle source map
await test(
'./__source__/__compiled__/no-columns/ComponentWithExternalCustomHooks',
); // simulated Webpack 'cheap-module-source-map'
});
await test(
'./__source__/__compiled__/inline/ComponentWithMultipleHooksPerLine',
); // inline source map
await test(
'./__source__/__compiled__/external/ComponentWithMultipleHooksPerLine',
); // external source map
await test(
'./__source__/__compiled__/inline/index-map/ComponentWithMultipleHooksPerLine',
); // inline index map source map
await test(
'./__source__/__compiled__/external/index-map/ComponentWithMultipleHooksPerLine',
); // external index map source map
await test(
'./__source__/__compiled__/bundle',
'ComponentWithMultipleHooksPerLine',
); // bundle source map
// Note that this test is expected to only match the first two hooks
// because the 3rd and 4th hook are on the same line,
// and this type of source map doesn't have column numbers.
await noColumnTest(
'./__source__/__compiled__/no-columns/ComponentWithMultipleHooksPerLine',
); // simulated Webpack 'cheap-module-source-map'
});
'./__source__/__compiled__/inline/index-map/ContainingStringSourceMappingURL',
); // inline index map source map
await test(
'./__source__/__compiled__/external/index-map/ContainingStringSourceMappingURL',
); // external index map source map
await test(
'./__source__/__compiled__/bundle',
'ContainingStringSourceMappingURL',
); // bundle source map
await test(
'./__source__/__compiled__/no-columns/ContainingStringSourceMappingURL',
); // simulated Webpack 'cheap-module-source-map'
});
});
await test(
'./__source__/__compiled__/inline/fb-sources-extended/Example',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/Example',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/Example',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/Example',
); // x_react_sources extended external source map
// TODO test no-columns and bundle cases with extended source maps
});
it('should work with more complex files and components', async () => {
async function test(path, name = undefined) {
const components = name != null ? require(path)[name] : require(path);
expect(require('@babel/parser').parse).toHaveBeenCalledTimes(0);
expect(require('../generateHookMap').decodeHookMap).toHaveBeenCalled();
}
await test(
'./__source__/__compiled__/inline/fb-sources-extended/ToDoList',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ToDoList',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ToDoList',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ToDoList',
); // x_react_sources extended external source map
// TODO test no-columns and bundle cases with extended source maps
});
await test(
'./__source__/__compiled__/inline/fb-sources-extended/ComponentWithCustomHook',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ComponentWithCustomHook',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ComponentWithCustomHook',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ComponentWithCustomHook'
,
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/ComponentWithCustom
Hook',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
ComponentWithCustomHook',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
ComponentWithCustomHook',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
ComponentWithCustomHook',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
'./__source__/__compiled__/inline/fb-sources-extended/ComponentUsingHooksIndirectly
',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ComponentUsingHooksIndirect
ly',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ComponentUsingHooksIndirec
tly',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ComponentUsingHooksIndir
ectly',
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/ComponentUsingHooks
Indirectly',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
ComponentUsingHooksIndirectly',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
ComponentUsingHooksIndirectly',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
ComponentUsingHooksIndirectly',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
await test(
'./__source__/__compiled__/inline/fb-sources-extended/ComponentWithNestedHooks',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ComponentWithNestedHooks',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ComponentWithNestedHooks',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ComponentWithNestedHooks
',
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/ComponentWithNested
Hooks',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
ComponentWithNestedHooks',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
ComponentWithNestedHooks',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
ComponentWithNestedHooks',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
// We can't test the uncompiled source here, because it either needs to get
transformed,
// which would break the source mapping, or the import statements will fail.
await test(
'./__source__/__compiled__/inline/fb-sources-extended/ComponentWithExternalCustomHo
oks',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ComponentWithExternalCustom
Hooks',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ComponentWithExternalCusto
mHooks',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ComponentWithExternalCus
tomHooks',
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/ComponentWithExtern
alCustomHooks',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
ComponentWithExternalCustomHooks',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
ComponentWithExternalCustomHooks',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
ComponentWithExternalCustomHooks',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
await test(
'./__source__/__compiled__/inline/fb-sources-extended/ComponentWithMultipleHooksPer
Line',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ComponentWithMultipleHooksP
erLine',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ComponentWithMultipleHooks
PerLine',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ComponentWithMultipleHoo
ksPerLine',
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/ComponentWithMultip
leHooksPerLine',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
ComponentWithMultipleHooksPerLine',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
ComponentWithMultipleHooksPerLine',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
ComponentWithMultipleHooksPerLine',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
await test(
'./__source__/__compiled__/inline/fb-sources-extended/InlineRequire',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/InlineRequire',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/InlineRequire',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/InlineRequire',
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/InlineRequire',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
InlineRequire',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
InlineRequire',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
InlineRequire',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
await test(
'./__source__/__compiled__/inline/fb-sources-extended/ContainingStringSourceMapping
URL',
); // x_facebook_sources extended inline source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/ContainingStringSourceMappi
ngURL',
); // x_facebook_sources extended external source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/ContainingStringSourceMapp
ingURL',
); // x_react_sources extended inline source map
await test(
'./__source__/__compiled__/external/react-sources-extended/ContainingStringSourceMa
ppingURL',
); // x_react_sources extended external source map
'./__source__/__compiled__/inline/fb-sources-extended/index-map/ContainingStringSou
rceMappingURL',
); // x_facebook_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/fb-sources-extended/index-map/
ContainingStringSourceMappingURL',
); // x_facebook_sources extended external index map source map
await test(
'./__source__/__compiled__/inline/react-sources-extended/index-map/
ContainingStringSourceMappingURL',
); // x_react_sources extended inline index map source map
await test(
'./__source__/__compiled__/external/react-sources-extended/index-map/
ContainingStringSourceMappingURL',
); // x_react_sources extended external index map source map
// TODO test no-columns and bundle cases with extended source maps
});
});
});
beforeEach(() => {
window.Worker = undefined;
workerizedParseSourceAndMetadataMock = jest.fn();
initFetchMock();
jest.mock('../parseHookNames/parseSourceAndMetadata.worker.js', () => {
return {
__esModule: true,
default: () => ({
parseSourceAndMetadata: workerizedParseSourceAndMetadataMock,
}),
};
});
inspectHooks =
require('react-debug-tools/src/ReactDebugHooks').inspectHooks;
parseHookNames = require('../parseHookNames').parseHookNames;
});
async function getHookNamesForComponent(Component, props = {}) {
const hooksTree = inspectHooks(Component, props, undefined, true);
const hookNames = await parseHookNames(hooksTree);
return hookNames;
}
window.Worker = true;
await getHookNamesForComponent(Component);
expect(workerizedParseSourceAndMetadataMock).toHaveBeenCalledTimes(1);
});
});