test: add golden file test for VSCode extension package contents (#64991)

This commit introduces a golden file test to verify the contents of the VSCode extension package.
The test ensures that the list of files included in the extension package remains consistent.

A new Bazel rule  is added to generate the list of files, and  is used to compare it against the golden file. A helper script  is also added to facilitate the generation of the golden file.

PR Close #64991
This commit is contained in:
Alan Agius 2025-11-07 13:56:41 +00:00 committed by Andrew Kushnir
parent 680c3c7bff
commit 74a38f00a0
5 changed files with 126 additions and 1 deletions

View file

@ -0,0 +1 @@
exports_files(["vsix_package_contents.txt"])

View file

@ -0,0 +1,27 @@
CHANGELOG.md
README.md
angular.png
index.js
node_modules/@angular/language-service/api.d.ts
node_modules/@angular/language-service/api_bundle.js
node_modules/@angular/language-service/bundles/language-service.js
node_modules/@angular/language-service/factory_bundle.js
node_modules/@angular/language-service/index.d.ts
node_modules/@angular/language-service/index.js
node_modules/@angular/language-service/package.json
node_modules/@angular/language-service/plugin-factory.d.ts
node_modules/typescript
package.json
server/README.md
server/bin/ngserver
server/index.js
server/package.json
syntaxes/expression.json
syntaxes/host-object-literal.json
syntaxes/inline-styles.json
syntaxes/inline-template.json
syntaxes/let-declaration.json
syntaxes/template-blocks.json
syntaxes/template-tag.json
syntaxes/template.json
syntaxes/tsconfig.json

View file

@ -1,5 +1,6 @@
load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template_rule")
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_file")
load("@aspect_rules_js//js:defs.bzl", "js_library", "js_run_binary")
load("@aspect_rules_js//npm:defs.bzl", "npm_package")
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
load("@npm//:defs.bzl", "npm_link_all_packages")
@ -104,3 +105,20 @@ npm_package(
],
visibility = ["//vscode-ng-language-service/integration/e2e:__subpackages__"],
)
js_run_binary(
name = "vsix_contents_golden_file",
srcs = [":vsix_sandbox"],
outs = ["vsix_package_contents.txt"],
args = [
"$(rootpath vsix_package_contents.txt)",
],
tool = "//vscode-ng-language-service/tools:vsix_contents_goldens",
)
write_source_file(
name = "vsix_contents_golden",
check_that_out_file_exists = False,
in_file = ":vsix_contents_golden_file",
out_file = "//goldens/vscode-extension:vsix_package_contents.txt",
)

View file

@ -0,0 +1,13 @@
load("@aspect_rules_js//js:defs.bzl", "js_binary")
js_binary(
name = "vsix_contents_goldens",
data = ["//vscode-ng-language-service:node_modules/@vscode/vsce"],
entry_point = ":vsix_contents_goldens.mjs",
# vsce requires npm on the PATH; we can get this from the Bazel rules_nodejs but it is not
# included by default in rules_js binary rules so we include it here explicitly
include_npm = True,
visibility = [
"//vscode-ng-language-service:__pkg__",
],
)

View file

@ -0,0 +1,66 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
// tslint:disable:no-console
import {writeFile} from 'node:fs/promises';
import {exec as nodeExec} from 'node:child_process';
import {promisify} from 'node:util';
import {join} from 'node:path';
const exec = promisify(nodeExec);
const rootPath = join(import.meta.dirname, '../');
async function main(outPath) {
// `vsce ls` needs to run with a CWD of what will be packaged.
// There is no way to provide this via args.
const {stdout, stderr} = await exec(
`node ${join(rootPath, `node_modules/@vscode/vsce/vsce`)} ls`,
{
cwd: join(rootPath, 'vsix_sandbox'),
},
);
if (stderr) {
console.error(stderr);
throw new Error('Failed with error. See above.');
}
const paths = stdout.trim().split('\n').filter(Boolean);
const resultSet = new Set();
for (const filePath of paths) {
if (filePath.startsWith('node_modules/') && !filePath.includes('@angular')) {
// Regex to capture 'node_modules/' followed by either:
// - A scoped package (e.g., @vscode/vsce)
// - A standard package (e.g., typescript)
// It stops matching before the next slash after the package name.
const match = filePath.match(/^(node_modules\/(?:@[^\/]+\/[^\/]+|[^\/]+))/);
if (match) {
resultSet.add(match[1]);
}
} else {
// Add non-node_modules or @angular/ paths directly
resultSet.add(filePath);
}
}
await writeFile(outPath, Array.from(resultSet).sort().join('\n'));
}
const argv = process.argv.slice(2);
if (argv.length !== 1) {
console.error('Must include 1 argument that specifies the output path.');
process.exit(1);
}
const [outPath] = argv;
main(outPath).catch((err) => {
console.error(err);
process.exit(1);
});