lion/packages/providence-analytics/test-node/program/analyzers/find-exports.test.js

254 lines
9.2 KiB
JavaScript

const { expect } = require('chai');
const { providence } = require('../../../src/program/providence.js');
const { QueryService } = require('../../../src/program/services/QueryService.js');
const {
mockProject,
restoreMockedProjects,
getEntry,
getEntries,
} = require('../../../test-helpers/mock-project-helpers.js');
const {
mockWriteToJson,
restoreWriteToJson,
} = require('../../../test-helpers/mock-report-service-helpers.js');
const {
suppressNonCriticalLogs,
restoreSuppressNonCriticalLogs,
} = require('../../../test-helpers/mock-log-service-helpers.js');
const findExportsQueryConfig = QueryService.getQueryConfigFromAnalyzer('find-exports');
describe('Analyzer "find-exports"', () => {
const queryResults = [];
const _providenceCfg = {
targetProjectPaths: ['/fictional/project'], // defined in mockProject
};
const cacheDisabledInitialValue = QueryService.cacheDisabled;
before(() => {
QueryService.cacheDisabled = true;
});
after(() => {
QueryService.cacheDisabled = cacheDisabledInitialValue;
});
beforeEach(() => {
suppressNonCriticalLogs();
mockWriteToJson(queryResults);
});
afterEach(() => {
restoreSuppressNonCriticalLogs();
restoreWriteToJson(queryResults);
restoreMockedProjects();
});
describe('Export notations', () => {
it(`supports [export const x = 0] (named specifier)`, async () => {
mockProject([`export const x = 0`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('x');
expect(firstEntry.result[0].source).to.be.null;
});
it(`supports [export default class X {}] (default export)`, async () => {
mockProject([`export default class X {}`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]');
expect(firstEntry.result[0].source).to.equal(undefined);
});
it(`supports [export { x } from 'my/source'] (re-export named specifier)`, async () => {
mockProject([`export { x } from 'my/source'`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('x');
expect(firstEntry.result[0].source).to.equal('my/source');
});
it(`supports [export { x as y } from 'my/source'] (re-export renamed specifier)`, async () => {
mockProject([`export { x as y } from 'my/source'`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('y');
expect(firstEntry.result[0].source).to.equal('my/source');
});
it(`stores meta info(local name) of renamed specifiers`, async () => {
mockProject([`export { x as y } from 'my/source'`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
// This info will be relevant later to identify 'transitive' relations
expect(firstEntry.result[0].localMap).to.eql([
{
local: 'x',
exported: 'y',
},
]);
});
it(`supports [export { x, y } from 'my/source'] (multiple re-exported named specifiers)`, async () => {
mockProject([`export { x, y } from 'my/source'`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(2);
expect(firstEntry.result[0].exportSpecifiers).to.eql(['x', 'y']);
expect(firstEntry.result[0].source).to.equal('my/source');
});
it(`stores rootFileMap of an exported Identifier`, async () => {
mockProject({
'./src/OriginalComp.js': `export class OriginalComp {}`,
'./src/inbetween.js': `export { OriginalComp as InBetweenComp } from './OriginalComp.js'`,
'./index.js': `export { InBetweenComp as MyComp } from './src/inbetween.js'`,
});
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
const secondEntry = getEntry(queryResult, 1);
const thirdEntry = getEntry(queryResult, 2);
expect(firstEntry.result[0].rootFileMap).to.eql([
{
currentFileSpecifier: 'MyComp', // this is the local name in the file we track from
rootFile: {
file: './src/OriginalComp.js', // the file containing declaration
specifier: 'OriginalComp', // the specifier that was exported in file
},
},
]);
expect(secondEntry.result[0].rootFileMap).to.eql([
{
currentFileSpecifier: 'InBetweenComp',
rootFile: {
file: './src/OriginalComp.js',
specifier: 'OriginalComp',
},
},
]);
expect(thirdEntry.result[0].rootFileMap).to.eql([
{
currentFileSpecifier: 'OriginalComp',
rootFile: {
file: '[current]',
specifier: 'OriginalComp',
},
},
]);
});
// TODO: myabe in the future: This experimental syntax requires enabling the parser plugin: 'exportDefaultFrom'
it.skip(`stores rootFileMap of an exported Identifier`, async () => {
mockProject({
'./src/reexport.js': `
// a direct default import
import RefDefault from 'exporting-ref-project';
export RefDefault;
`,
'./index.js': `
export { ExtendRefDefault } from './src/reexport.js';
`,
});
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].rootFileMap).to.eql([
{
currentFileSpecifier: 'ExtendRefDefault',
rootFile: {
file: 'exporting-ref-project',
specifier: '[default]',
},
},
]);
});
});
describe('Export variable types', () => {
it(`classes`, async () => {
mockProject([`export class X {}`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('X');
expect(firstEntry.result[0].source).to.be.null;
});
it(`functions`, async () => {
mockProject([`export function y() {}`]);
await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('y');
expect(firstEntry.result[0].source).to.be.null;
});
// ...etc?
// ...TODO: create custom hooks to store meta info about types etc.
});
describe('Default post processing', () => {
// onlyInternalSources: false,
// keepOriginalSourcePaths: false,
// filterSpecifier: null,
});
describe('Options', () => {
// TODO: Move to dashboard
it.skip(`"metaConfig.categoryConfig"`, async () => {
mockProject(
[
`export const foo = null`, // firstEntry
`export const bar = null`, // secondEntry
`export const baz = null`, // thirdEntry
],
{
projectName: 'my-project',
filePaths: ['./foo.js', './packages/bar/test/bar.test.js', './temp/baz.js'],
},
);
const findExportsCategoryQueryObj = QueryService.getQueryConfigFromAnalyzer('find-exports', {
metaConfig: {
categoryConfig: [
{
project: 'my-project',
categories: {
fooCategory: localFilePath => localFilePath.startsWith('./foo'),
barCategory: localFilePath => localFilePath.startsWith('./packages/bar'),
testCategory: localFilePath => localFilePath.includes('/test/'),
},
},
],
},
});
await providence(findExportsCategoryQueryObj, _providenceCfg);
const queryResult = queryResults[0];
const [firstEntry, secondEntry, thirdEntry] = getEntries(queryResult);
expect(firstEntry.meta.categories).to.eql(['fooCategory']);
// not mutually exclusive...
expect(secondEntry.meta.categories).to.eql(['barCategory', 'testCategory']);
expect(thirdEntry.meta.categories).to.eql([]);
});
});
});