mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
490 lines
16 KiB
TypeScript
490 lines
16 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { expect } from 'chai';
|
|
|
|
import {
|
|
MemberList,
|
|
dedup,
|
|
mapSymbolToType,
|
|
replaceAll,
|
|
writeReportToFile,
|
|
ErrorCode,
|
|
writeReportToDirectory,
|
|
extractExternalDependencies,
|
|
Report,
|
|
extractExports,
|
|
extractAllTopLevelSymbols
|
|
} from '../analysis-helper';
|
|
|
|
import {
|
|
getTestModuleDtsFilePath,
|
|
getAssortedImportsJsFilePath,
|
|
getSubsetExportsBundleFilePath
|
|
} from './utils';
|
|
import * as fs from 'fs';
|
|
import { resolve } from 'path';
|
|
|
|
describe('extractExports', () => {
|
|
let testModuleDtsFile: string;
|
|
let extractedDeclarations: MemberList;
|
|
before(() => {
|
|
const start = Date.now();
|
|
testModuleDtsFile = getTestModuleDtsFilePath();
|
|
extractedDeclarations = extractExports(testModuleDtsFile);
|
|
console.log('extractExports took ', Date.now() - start);
|
|
});
|
|
// export {tar as tarr, tar1 as tarr1} from '..'
|
|
it('test export rename', () => {
|
|
expect(extractedDeclarations.functions).to.include.members([
|
|
'tarr',
|
|
'tarr1'
|
|
]);
|
|
});
|
|
// function foo() { }
|
|
// export { foo as foo2 };
|
|
it('test declare then export', () => {
|
|
expect(extractedDeclarations.functions).to.include.members(['foo2']);
|
|
expect(extractedDeclarations.classes).to.include.members(['Foo1']);
|
|
});
|
|
|
|
it('test basic variable extractions', () => {
|
|
expect(extractedDeclarations.variables).to.include.members([
|
|
'basicVarDeclarationExport',
|
|
'basicVarStatementExport',
|
|
'reExportVarStatementExport'
|
|
]);
|
|
});
|
|
it('test re-exported variable extractions from same module - named re-exports', () => {
|
|
expect(extractedDeclarations.variables).to.include.members([
|
|
'basicVarDeclarationExportFar',
|
|
'basicVarStatementExportFar',
|
|
'reExportVarStatementExportFar'
|
|
]);
|
|
});
|
|
it('test re-exported variable extractions from same module - * re-exports', () => {
|
|
expect(extractedDeclarations.variables).to.include.members([
|
|
'basicVarDeclarationExportBar',
|
|
'basicVarStatementExportBar',
|
|
'reExportVarStatementExportBar'
|
|
]);
|
|
});
|
|
|
|
it('test basic function extractions', () => {
|
|
expect(extractedDeclarations.functions).to.include.members([
|
|
'basicFuncExportNoDependencies',
|
|
'basicFuncExportVarDependencies',
|
|
'basicFuncExportFuncDependencies',
|
|
'basicFuncExportEnumDependencies',
|
|
'basicFuncExternalDependencies'
|
|
]);
|
|
});
|
|
it('test basic function de-duplication ', () => {
|
|
expect(extractedDeclarations.functions).include.members([
|
|
'basicUniqueFunc'
|
|
]);
|
|
|
|
expect(
|
|
extractedDeclarations.functions.filter(
|
|
each => each.localeCompare('basicUniqueFunc') === 0
|
|
).length
|
|
).to.equal(1);
|
|
});
|
|
|
|
it('test re-exported function extractions from same module - named re-exports', () => {
|
|
expect(extractedDeclarations.functions).to.include.members([
|
|
'basicFuncExportNoDependenciesFar',
|
|
'basicFuncExportVarDependenciesFar',
|
|
'basicFuncExportFuncDependenciesFar',
|
|
'basicFuncExportEnumDependenciesFar',
|
|
'basicFuncExternalDependenciesFar'
|
|
]);
|
|
});
|
|
|
|
it('test re-exported function extractions from same module - * re-exports', () => {
|
|
expect(extractedDeclarations.functions).to.include.members([
|
|
'basicFuncExportNoDependenciesBar',
|
|
'basicFuncExportVarDependenciesBar',
|
|
'basicFuncExportFuncDependenciesBar',
|
|
'basicFuncExportEnumDependenciesBar',
|
|
'basicFuncExternalDependenciesBar'
|
|
]);
|
|
});
|
|
|
|
it('test re-exported function de-duplication from same module ', () => {
|
|
expect(extractedDeclarations.functions).include.members([
|
|
'basicUniqueFuncFar'
|
|
]);
|
|
|
|
expect(
|
|
extractedDeclarations.functions.filter(
|
|
each => each.localeCompare('basicUniqueFuncFar') === 0
|
|
).length
|
|
).to.equal(1);
|
|
});
|
|
|
|
it('test basic class extractions', () => {
|
|
expect(extractedDeclarations.classes).to.include.members([
|
|
'BasicClassExport'
|
|
]);
|
|
});
|
|
|
|
it('test re-exported class extractions from same module - named re-exports', () => {
|
|
expect(extractedDeclarations.classes).to.include.members([
|
|
'BasicClassExportFar'
|
|
]);
|
|
});
|
|
it('test re-exported class extractions from same module - * re-exports', () => {
|
|
expect(extractedDeclarations.classes).to.include.members([
|
|
'BasicClassExportBar'
|
|
]);
|
|
});
|
|
|
|
it('test basic enum extractions', () => {
|
|
expect(extractedDeclarations.enums).to.include.members(['BasicEnumExport']);
|
|
});
|
|
|
|
it('test re-exported enum extractions from same module - named re-exports', () => {
|
|
expect(extractedDeclarations.enums).to.include.members([
|
|
'BasicEnumExportFar'
|
|
]);
|
|
});
|
|
it('test re-exported enum extractions from same module - * re-exports', () => {
|
|
expect(extractedDeclarations.enums).to.include.members([
|
|
'BasicEnumExportBar'
|
|
]);
|
|
});
|
|
// import {LogLevel as LogLevel1} from '@firebase/logger';
|
|
// export {LogLevel1 as LogLevel2};
|
|
it('test renamed import then renamed export', () => {
|
|
expect(extractedDeclarations.enums).to.include.members(['LogLevel2']);
|
|
});
|
|
|
|
//import { Logger } from "@firebase/logger";
|
|
// export { Logger as Logger1 };
|
|
it('test import then renamed export', () => {
|
|
expect(extractedDeclarations.classes).to.include.members(['Logger1']);
|
|
});
|
|
|
|
//import { setLogLevel } from "@firebase/logger";
|
|
// export { setLogLevel };
|
|
it('test import then export', () => {
|
|
expect(extractedDeclarations.functions).to.include.members(['setLogLevel']);
|
|
});
|
|
|
|
// import * as fs from 'fs'
|
|
// export { fs as fs1 };
|
|
it('test namespace export', () => {
|
|
expect(extractedDeclarations.unknown).to.include.members(['fs1']);
|
|
});
|
|
});
|
|
|
|
describe('extractAllTopLevelSymbols', () => {
|
|
let subsetExportsBundleFile: string;
|
|
let extractedDeclarations: MemberList;
|
|
before(() => {
|
|
const start = Date.now();
|
|
subsetExportsBundleFile = getSubsetExportsBundleFilePath();
|
|
extractedDeclarations = extractAllTopLevelSymbols(subsetExportsBundleFile);
|
|
console.log(
|
|
'extractDeclarations on js bundle file took ',
|
|
Date.now() - start
|
|
);
|
|
});
|
|
it('test variable extractions', () => {
|
|
const variablesArray = ['aVar'];
|
|
variablesArray.sort();
|
|
expect(extractedDeclarations.variables).to.include.members(variablesArray);
|
|
});
|
|
|
|
it('test functions extractions', () => {
|
|
const functionsArray = [
|
|
'tar',
|
|
'tar1',
|
|
'basicFuncExportEnumDependencies',
|
|
'd1',
|
|
'd2',
|
|
'd3',
|
|
'basicFuncExportFuncDependenciesBar'
|
|
];
|
|
functionsArray.sort();
|
|
expect(extractedDeclarations.functions).to.have.members(functionsArray);
|
|
});
|
|
|
|
it('test enums extractions', () => {
|
|
const enumsArray = [
|
|
'BasicEnumExport',
|
|
'BasicEnumExportBar',
|
|
'BasicEnumExportFar'
|
|
];
|
|
enumsArray.sort();
|
|
expect(extractedDeclarations.variables).to.include.members(enumsArray);
|
|
});
|
|
|
|
it('test classes extractions', () => {
|
|
const classesArray = ['BasicClassExport'];
|
|
classesArray.sort();
|
|
expect(extractedDeclarations.classes).to.have.members(classesArray);
|
|
});
|
|
});
|
|
|
|
describe('test dedup helper function', () => {
|
|
it('test dedup with non-empty entries', () => {
|
|
let memberList: MemberList = {
|
|
functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'],
|
|
classes: ['aClass', 'bClass', 'aClass', 'cClass'],
|
|
variables: ['aVar', 'bVar', 'cVar', 'aVar'],
|
|
enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'],
|
|
unknown: []
|
|
};
|
|
memberList = dedup(memberList);
|
|
|
|
expect(memberList.functions).to.have.length(3);
|
|
expect(memberList.classes).to.have.length(3);
|
|
expect(memberList.variables).to.have.length(3);
|
|
expect(memberList.enums).to.have.length(4);
|
|
expect(
|
|
memberList.functions.filter(each => each.localeCompare('aFunc') === 0)
|
|
.length
|
|
).to.equal(1);
|
|
expect(
|
|
memberList.classes.filter(each => each.localeCompare('aClass') === 0)
|
|
.length
|
|
).to.equal(1);
|
|
expect(
|
|
memberList.variables.filter(each => each.localeCompare('aVar') === 0)
|
|
.length
|
|
).to.equal(1);
|
|
expect(
|
|
memberList.enums.filter(each => each.localeCompare('aEnum') === 0).length
|
|
).to.equal(1);
|
|
});
|
|
|
|
it('test dedup with empty entries', () => {
|
|
let memberList: MemberList = {
|
|
functions: [],
|
|
classes: [],
|
|
variables: ['aVar', 'bVar', 'cVar', 'aVar'],
|
|
enums: [],
|
|
unknown: []
|
|
};
|
|
memberList = dedup(memberList);
|
|
expect(memberList.functions).to.have.length(0);
|
|
expect(memberList.classes).to.have.length(0);
|
|
expect(memberList.enums).to.have.length(0);
|
|
expect(memberList.variables).to.have.length(3);
|
|
|
|
expect(
|
|
memberList.variables.filter(each => each.localeCompare('aVar') === 0)
|
|
.length
|
|
).to.equal(1);
|
|
});
|
|
});
|
|
|
|
describe('test replaceAll helper function', () => {
|
|
it('test replaceAll with multiple occurrences of an element', () => {
|
|
const memberList: MemberList = {
|
|
functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'],
|
|
classes: ['aClass', 'bClass', 'aClass', 'cClass'],
|
|
variables: ['aVar', 'bVar', 'cVar', 'aVar'],
|
|
enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'],
|
|
unknown: []
|
|
};
|
|
const original: string = 'aFunc';
|
|
const replaceTo: string = 'replacedFunc';
|
|
replaceAll(memberList, original, replaceTo);
|
|
expect(memberList.functions).to.not.include.members([original]);
|
|
expect(memberList.functions).to.include.members([replaceTo]);
|
|
expect(memberList.functions).to.have.length(4);
|
|
expect(
|
|
memberList.functions.filter(each => each.localeCompare(original) === 0)
|
|
.length
|
|
).to.equal(0);
|
|
expect(
|
|
memberList.functions.filter(each => each.localeCompare(replaceTo) === 0)
|
|
.length
|
|
).to.equal(2);
|
|
});
|
|
|
|
it('test replaceAll with single occurrence of an element', () => {
|
|
const memberList: MemberList = {
|
|
functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'],
|
|
classes: ['aClass', 'bClass', 'aClass', 'cClass'],
|
|
variables: ['aVar', 'bVar', 'cVar', 'aVar'],
|
|
enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'],
|
|
unknown: []
|
|
};
|
|
const replaceTo: string = 'replacedClass';
|
|
const original: string = 'bClass';
|
|
replaceAll(memberList, original, replaceTo);
|
|
expect(memberList.classes).to.not.include.members([original]);
|
|
expect(memberList.classes).to.include.members([replaceTo]);
|
|
expect(memberList.classes).to.have.length(4);
|
|
expect(
|
|
memberList.classes.filter(each => each.localeCompare(original) === 0)
|
|
.length
|
|
).to.equal(0);
|
|
expect(
|
|
memberList.classes.filter(each => each.localeCompare(replaceTo) === 0)
|
|
.length
|
|
).to.equal(1);
|
|
});
|
|
|
|
it('test replaceAll with zero occurrence of an element', () => {
|
|
const memberList: MemberList = {
|
|
functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'],
|
|
classes: ['aClass', 'bClass', 'aClass', 'cClass'],
|
|
variables: ['aVar', 'bVar', 'cVar', 'aVar'],
|
|
enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'],
|
|
unknown: []
|
|
};
|
|
const replaceTo: string = 'replacedEnum';
|
|
const original: string = 'eEnum';
|
|
replaceAll(memberList, original, replaceTo);
|
|
expect(memberList.enums).to.not.include.members([original, replaceTo]);
|
|
expect(memberList.enums).to.have.length(4);
|
|
expect(
|
|
memberList.enums.filter(each => each.localeCompare(original) === 0).length
|
|
).to.equal(0);
|
|
expect(
|
|
memberList.enums.filter(each => each.localeCompare(replaceTo) === 0)
|
|
.length
|
|
).to.equal(0);
|
|
});
|
|
});
|
|
|
|
describe('test mapSymbolToType helper function', () => {
|
|
it('test if function correctly categorizes symbols that are misplaced', () => {
|
|
let memberList: MemberList = {
|
|
functions: ['aVar', 'bFunc', 'cFunc'],
|
|
classes: ['bClass', 'cClass'],
|
|
variables: ['aClass', 'bVar', 'cVar', 'aEnum'],
|
|
enums: ['bEnum', 'cEnum', 'dEnum', 'aFunc'],
|
|
unknown: []
|
|
};
|
|
|
|
const map: Map<string, string> = new Map([
|
|
['aFunc', 'functions'],
|
|
['bFunc', 'functions'],
|
|
['aClass', 'classes'],
|
|
['bClass', 'classes'],
|
|
['aVar', 'variables'],
|
|
['bVar', 'variables'],
|
|
['aEnum', 'enums']
|
|
]);
|
|
|
|
memberList = mapSymbolToType(map, memberList);
|
|
|
|
expect(memberList.functions).to.have.members(['aFunc', 'bFunc', 'cFunc']);
|
|
expect(memberList.functions).to.not.include.members(['aVar']);
|
|
expect(memberList.classes).to.have.members(['aClass', 'bClass', 'cClass']);
|
|
expect(memberList.variables).to.not.include.members(['aClass', 'aEnum']);
|
|
expect(memberList.variables).to.have.members(['aVar', 'bVar', 'cVar']);
|
|
expect(memberList.enums).to.have.members([
|
|
'aEnum',
|
|
'bEnum',
|
|
'cEnum',
|
|
'dEnum'
|
|
]);
|
|
expect(memberList.enums).to.not.include.members(['aFunc']);
|
|
|
|
expect(memberList.functions).to.have.length(3);
|
|
expect(memberList.classes).to.have.length(3);
|
|
expect(memberList.variables).to.have.length(3);
|
|
expect(memberList.enums).to.have.length(4);
|
|
});
|
|
});
|
|
|
|
describe('test writeReportToFile helper function', () => {
|
|
let fileContent: Report;
|
|
|
|
before(() => {
|
|
fileContent = {
|
|
name: 'name',
|
|
symbols: []
|
|
};
|
|
});
|
|
it('should throw error when given path exists and points to directory', () => {
|
|
const aDir = resolve('./a-dir/a-sub-dir');
|
|
fs.mkdirSync(aDir, { recursive: true });
|
|
expect(() => writeReportToFile(fileContent, aDir)).to.throw(
|
|
ErrorCode.OUTPUT_FILE_REQUIRED
|
|
);
|
|
});
|
|
|
|
it('should not throw error when given path does not pre-exist', () => {
|
|
const aPathToFile = resolve('./a-dir/a-sub-dir/a-file');
|
|
expect(() => writeReportToFile(fileContent, aPathToFile)).to.not.throw();
|
|
fs.unlinkSync(aPathToFile);
|
|
});
|
|
after(() => {
|
|
fs.rmdirSync('a-dir/a-sub-dir');
|
|
fs.rmdirSync('a-dir', { recursive: true });
|
|
});
|
|
});
|
|
|
|
describe('test writeReportToDirectory helper function', () => {
|
|
let fileContent: Report;
|
|
|
|
before(() => {
|
|
fileContent = {
|
|
name: 'name',
|
|
symbols: []
|
|
};
|
|
});
|
|
it('should throw error when given path exists and points to a file', () => {
|
|
const aDir = resolve('./a-dir/a-sub-dir');
|
|
fs.mkdirSync(aDir, { recursive: true });
|
|
const aFile = `a-file`;
|
|
const aPathToFile = `${aDir}/${aFile}`;
|
|
fs.writeFileSync(aPathToFile, JSON.stringify(fileContent));
|
|
expect(() =>
|
|
writeReportToDirectory(fileContent, aFile, aPathToFile)
|
|
).to.throw(ErrorCode.OUTPUT_DIRECTORY_REQUIRED);
|
|
});
|
|
|
|
it('should not throw error when given path does not pre-exist', () => {
|
|
const aDir = resolve('./a-dir/a-sub-dir');
|
|
const aFile = `a-file`;
|
|
expect(() =>
|
|
writeReportToDirectory(fileContent, aFile, aDir)
|
|
).to.not.throw();
|
|
});
|
|
after(() => {
|
|
fs.unlinkSync(`${resolve('./a-dir/a-sub-dir')}/a-file`);
|
|
fs.rmdirSync('a-dir/a-sub-dir');
|
|
fs.rmdirSync('a-dir', { recursive: true });
|
|
});
|
|
});
|
|
|
|
describe('test extractExternalDependencies helper function', () => {
|
|
it('should correctly extract all symbols listed in import statements', () => {
|
|
const assortedImports: string = getAssortedImportsJsFilePath();
|
|
const externals: { [key: string]: string[] } =
|
|
extractExternalDependencies(assortedImports);
|
|
|
|
expect(externals['./bar']).to.have.members([
|
|
'basicFuncExternalDependenciesBar',
|
|
'basicFuncExportEnumDependenciesBar',
|
|
'BasicClassExportBar' // extract original name if renamed
|
|
]);
|
|
expect(externals['@firebase/logger']).to.be.undefined;
|
|
expect(externals['fs']).to.have.members(['*']); // namespace export
|
|
// expect(externals['@firebase/app']).to.have.members(['default export']); // default export
|
|
});
|
|
});
|