feat(dev-infra): add option to setup global types in API golden test (#42876)
Previously we disabled automatic type-resolution for the API extractor because in non-sandbox environments this resulted in different API reports. There are cases where global types are still needed for analysis of an entry-point. To support this, we add a new property called `types` which allows for explicit type targets being specified. Note that we do not want to determine types from the `data` runfiles because API extractor itself also brings in types which should not always be part of the API report analysis. PR Close #42876
This commit is contained in:
parent
7271ad15ba
commit
7eddd12bf0
|
@ -17,6 +17,27 @@ default_strip_export_pattern = "^ɵ(?!ɵdefineInjectable|ɵinject|ɵInjectableDe
|
|||
def _escape_regex_for_arg(value):
|
||||
return "\"%s\"" % value
|
||||
|
||||
"""
|
||||
Extracts type names from a list of NPM type targets.
|
||||
|
||||
For example: Consider the `@npm//@types/node` target. This function extracts `node`
|
||||
from the label. This is needed so that the Node types can be wired up within a
|
||||
TypeScript program using the `types` tsconfig option.
|
||||
"""
|
||||
|
||||
def extract_type_names_from_labels(type_targets):
|
||||
type_names = []
|
||||
for type_target in type_targets:
|
||||
type_package = Label(type_target).package
|
||||
|
||||
if (type_package.startswith("@types/")):
|
||||
type_names.append(type_package[len("@types/"):])
|
||||
else:
|
||||
fail("Expected type target to match the following format: " +
|
||||
"`@<npm_workspace>//@types/<name>`, but got: %s" % type_target)
|
||||
|
||||
return type_names
|
||||
|
||||
"""
|
||||
Builds an API report for the specified entry-point and compares it against the
|
||||
specified golden
|
||||
|
@ -28,6 +49,7 @@ def api_golden_test(
|
|||
entry_point,
|
||||
data = [],
|
||||
strip_export_pattern = default_strip_export_pattern,
|
||||
types = [],
|
||||
**kwargs):
|
||||
quoted_export_pattern = _escape_regex_for_arg(strip_export_pattern)
|
||||
|
||||
|
@ -46,13 +68,15 @@ def api_golden_test(
|
|||
include_default_files = False,
|
||||
)
|
||||
|
||||
test_data = ["//dev-infra/bazel/api-golden", "//:package.json", ":%s_data_typings" % name] + data
|
||||
test_data = ["//dev-infra/bazel/api-golden", "//:package.json", ":%s_data_typings" % name] + \
|
||||
data + types
|
||||
|
||||
nodejs_test(
|
||||
name = name,
|
||||
data = test_data,
|
||||
entry_point = "//dev-infra/bazel/api-golden:index.ts",
|
||||
templated_args = nodejs_test_args + [golden, entry_point, "false", quoted_export_pattern],
|
||||
templated_args = nodejs_test_args + [golden, entry_point, "false", quoted_export_pattern] +
|
||||
extract_type_names_from_labels(types),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
@ -61,7 +85,8 @@ def api_golden_test(
|
|||
testonly = True,
|
||||
data = test_data,
|
||||
entry_point = "//dev-infra/bazel/api-golden:index.ts",
|
||||
templated_args = nodejs_test_args + [golden, entry_point, "true", quoted_export_pattern],
|
||||
templated_args = nodejs_test_args + [golden, entry_point, "true", quoted_export_pattern] +
|
||||
extract_type_names_from_labels(types),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
@ -76,6 +101,7 @@ def api_golden_test_npm_package(
|
|||
npm_package,
|
||||
data = [],
|
||||
strip_export_pattern = default_strip_export_pattern,
|
||||
types = [],
|
||||
**kwargs):
|
||||
quoted_export_pattern = _escape_regex_for_arg(strip_export_pattern)
|
||||
|
||||
|
@ -83,17 +109,19 @@ def api_golden_test_npm_package(
|
|||
|
||||
nodejs_test(
|
||||
name = name,
|
||||
data = ["//dev-infra/bazel/api-golden"] + data,
|
||||
data = ["//dev-infra/bazel/api-golden"] + data + types,
|
||||
entry_point = "//dev-infra/bazel/api-golden:index_npm_packages.ts",
|
||||
templated_args = nodejs_test_args + [golden_dir, npm_package, "false", quoted_export_pattern],
|
||||
templated_args = nodejs_test_args + [golden_dir, npm_package, "false", quoted_export_pattern] +
|
||||
extract_type_names_from_labels(types),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = name + ".accept",
|
||||
testonly = True,
|
||||
data = ["//dev-infra/bazel/api-golden"] + data,
|
||||
data = ["//dev-infra/bazel/api-golden"] + data + types,
|
||||
entry_point = "//dev-infra/bazel/api-golden:index_npm_packages.ts",
|
||||
templated_args = nodejs_test_args + [golden_dir, npm_package, "true", quoted_export_pattern],
|
||||
templated_args = nodejs_test_args + [golden_dir, npm_package, "true", quoted_export_pattern] +
|
||||
extract_type_names_from_labels(types),
|
||||
**kwargs
|
||||
)
|
||||
|
|
|
@ -17,9 +17,9 @@ import {testApiGolden} from './test_api_report';
|
|||
*/
|
||||
async function main(
|
||||
goldenFilePath: string, entryPointFilePath: string, approveGolden: boolean,
|
||||
stripExportPattern: RegExp) {
|
||||
const {succeeded, apiReportChanged} =
|
||||
await testApiGolden(goldenFilePath, entryPointFilePath, approveGolden, stripExportPattern);
|
||||
stripExportPattern: RegExp, typeNames: string[]) {
|
||||
const {succeeded, apiReportChanged} = await testApiGolden(
|
||||
goldenFilePath, entryPointFilePath, approveGolden, stripExportPattern, typeNames);
|
||||
|
||||
if (!succeeded && apiReportChanged) {
|
||||
console.error(chalk.red(`The API signature has changed and the golden file is outdated.`));
|
||||
|
@ -37,9 +37,11 @@ if (require.main === module) {
|
|||
const entryPointFilePath = runfiles.resolve(args[1]);
|
||||
const approveGolden = args[2] === 'true';
|
||||
const stripExportPattern = new RegExp(args[3]);
|
||||
const typeNames = args.slice(4);
|
||||
|
||||
main(goldenFilePath, entryPointFilePath, approveGolden, stripExportPattern).catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
main(goldenFilePath, entryPointFilePath, approveGolden, stripExportPattern, typeNames)
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import {testApiGolden} from './test_api_report';
|
|||
* against golden files within the given golden directory.
|
||||
*/
|
||||
async function main(
|
||||
goldenDir: string, npmPackageDir: string, approveGolden: boolean, stripExportPattern: RegExp) {
|
||||
goldenDir: string, npmPackageDir: string, approveGolden: boolean, stripExportPattern: RegExp,
|
||||
typeNames: string[]) {
|
||||
const entryPoints = findEntryPointsWithinNpmPackage(npmPackageDir);
|
||||
const outdatedGoldens: string[] = [];
|
||||
let allTestsSucceeding = true;
|
||||
|
@ -35,7 +36,8 @@ async function main(
|
|||
const goldenFilePath = join(goldenDir, goldenName);
|
||||
|
||||
const {succeeded, apiReportChanged} = await testApiGolden(
|
||||
goldenFilePath, typesEntryPointPath, approveGolden, stripExportPattern, packageJsonPath);
|
||||
goldenFilePath, typesEntryPointPath, approveGolden, stripExportPattern, typeNames,
|
||||
packageJsonPath);
|
||||
|
||||
// Keep track of outdated goldens.
|
||||
if (!succeeded && apiReportChanged) {
|
||||
|
@ -63,8 +65,9 @@ if (require.main === module) {
|
|||
const npmPackageDir = runfiles.resolve(args[1]);
|
||||
const approveGolden = args[2] === 'true';
|
||||
const stripExportPattern = new RegExp(args[3]);
|
||||
const typeNames = args.slice(4);
|
||||
|
||||
main(goldenDir, npmPackageDir, approveGolden, stripExportPattern).catch(e => {
|
||||
main(goldenDir, npmPackageDir, approveGolden, stripExportPattern, typeNames).catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
|
@ -28,13 +28,15 @@ const _origFetchAstModuleExportInfo = ExportAnalyzer.prototype.fetchAstModuleExp
|
|||
* @param approveGolden Whether the golden file should be updated.
|
||||
* @param stripExportPattern Regular Expression that can be used to filter out exports
|
||||
* from the API report.
|
||||
* @param typeNames Name of types which should be included for analysis of the entry-point.
|
||||
* Types are expected to exist within the default `node_modules/@types/` folder.
|
||||
* @param packageJsonPath Optional path to a `package.json` file that contains the entry
|
||||
* point. Note that the `package.json` is currently only used by `api-extractor` to determine
|
||||
* the package name displayed within the API golden.
|
||||
*/
|
||||
export async function testApiGolden(
|
||||
goldenFilePath: string, indexFilePath: string, approveGolden: boolean,
|
||||
stripExportPattern: RegExp,
|
||||
stripExportPattern: RegExp, typeNames: string[] = [],
|
||||
packageJsonPath = resolveWorkspacePackageJsonPath()): Promise<ExtractorResult> {
|
||||
// If no `TEST_TMPDIR` is defined, then this script runs using `bazel run`. We use
|
||||
// the runfile directory as temporary directory for API extractor.
|
||||
|
@ -43,9 +45,10 @@ export async function testApiGolden(
|
|||
const configObject: IConfigFile = {
|
||||
compiler: {
|
||||
overrideTsconfig:
|
||||
// We disable inclusion of all `@types` installed as this throws-off API reports
|
||||
// and causes different goldens when the API test is run outside sandbox.
|
||||
{files: [indexFilePath], compilerOptions: {types: [], lib: ['esnext', 'dom']}}
|
||||
// We disable automatic `@types` resolution as this throws-off API reports
|
||||
// when the API test is run outside sandbox. Instead we expect a list of
|
||||
// hard-coded types that should be included. This works in non-sandbox too.
|
||||
{files: [indexFilePath], compilerOptions: {types: typeNames, lib: ['esnext', 'dom']}}
|
||||
},
|
||||
projectFolder: dirname(packageJsonPath),
|
||||
mainEntryPointFilePath: indexFilePath,
|
||||
|
|
Loading…
Reference in New Issue