diff --git a/src/python/pants/backend/javascript/goals/BUILD b/src/python/pants/backend/javascript/goals/BUILD index 366f2a56cff..fcba039c7b1 100644 --- a/src/python/pants/backend/javascript/goals/BUILD +++ b/src/python/pants/backend/javascript/goals/BUILD @@ -5,15 +5,25 @@ python_sources() python_tests( name="tests", - # The package.json files are inlined in ./test_integration_tests.py overrides={ "test_integration_test.py": { - "dependencies": ["./jest_resources", "./mocha_resources"], + "dependencies": [":jest_resources", ":mocha_resources"], "timeout": 240, }, "export_test.py": { + "dependencies": [":jest_resources"], "timeout": 240, "tags": ["platform_specific_behavior"], }, }, ) + +resources( + name="jest_resources", + sources=["test_resources/jest_project/**"], +) + +resources( + name="mocha_resources", + sources=["test_resources/mocha_project/**"], +) diff --git a/src/python/pants/backend/javascript/goals/export_test.py b/src/python/pants/backend/javascript/goals/export_test.py index c4c30045bda..3dabac1895d 100644 --- a/src/python/pants/backend/javascript/goals/export_test.py +++ b/src/python/pants/backend/javascript/goals/export_test.py @@ -58,7 +58,7 @@ def test_export_node_modules(rule_runner: RuleRunner) -> None: { "BUILD": "package_json(name='root')", "package-lock.json": ( - Path(__file__).parent / "jest_resources/package-lock.json" + Path(__file__).parent / "test_resources/jest_project/package-lock.json" ).read_text(), "package.json": given_package_with_name("ham"), } diff --git a/src/python/pants/backend/javascript/goals/jest_resources/BUILD b/src/python/pants/backend/javascript/goals/jest_resources/BUILD deleted file mode 100644 index dba6de52adb..00000000000 --- a/src/python/pants/backend/javascript/goals/jest_resources/BUILD +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -# Delete and re-generate these files using the following commands (updating versions as needed): -# corepack npm@11.6.2 install --package-lock-only -# corepack pnpm@10.19.0 install --lockfile-only -# corepack yarn@1.22.22 install --mode update-lockfile -resources(sources=["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) diff --git a/src/python/pants/backend/javascript/goals/mocha_resources/BUILD b/src/python/pants/backend/javascript/goals/mocha_resources/BUILD deleted file mode 100644 index dba6de52adb..00000000000 --- a/src/python/pants/backend/javascript/goals/mocha_resources/BUILD +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -# Delete and re-generate these files using the following commands (updating versions as needed): -# corepack npm@11.6.2 install --package-lock-only -# corepack pnpm@10.19.0 install --lockfile-only -# corepack yarn@1.22.22 install --mode update-lockfile -resources(sources=["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) diff --git a/src/python/pants/backend/javascript/goals/test_integration_test.py b/src/python/pants/backend/javascript/goals/test_integration_test.py index 2141dae3f59..53cf28889ae 100644 --- a/src/python/pants/backend/javascript/goals/test_integration_test.py +++ b/src/python/pants/backend/javascript/goals/test_integration_test.py @@ -17,6 +17,7 @@ JSTestRequest, TestMetadata, ) +from pants.backend.javascript.goals.testutil import load_js_test_project from pants.backend.javascript.package_json import PackageJsonTarget from pants.backend.javascript.target_types import ( JSSourcesGeneratorTarget, @@ -79,40 +80,12 @@ def rule_runner(package_manager: str) -> RuleRunner: return rule_runner -_LOCKFILE_FILE_NAMES = { - "pnpm": "pnpm-lock.yaml", - "npm": "package-lock.json", - "yarn": "yarn.lock", -} - - -def _find_lockfile_resource(package_manager: str, resource_dir: str) -> dict[str, str]: - for file in (Path(__file__).parent / resource_dir).iterdir(): - if _LOCKFILE_FILE_NAMES.get(package_manager) == file.name: - return {file.name: file.read_text()} - raise AssertionError( - f"No lockfile for {package_manager} set up in test resouces directory {resource_dir}." - ) - - -@pytest.fixture -def jest_lockfile(package_manager: str) -> dict[str, str]: - return _find_lockfile_resource(package_manager, "jest_resources") - - @pytest.fixture def jest_dev_dependencies() -> dict[str, str]: - return {"@types/jest": "30.0.0", "jest": "30.2.0", "ts-jest": "29.4.5", "typescript": "5.9.3"} - - -@pytest.fixture -def mocha_lockfile(package_manager: str) -> dict[str, str]: - return _find_lockfile_resource(package_manager, "mocha_resources") - - -@pytest.fixture -def mocha_dev_dependencies() -> dict[str, str]: - return {"mocha": "11.7.4"} + pkg = json.loads( + (Path(__file__).parent / "test_resources/jest_project/package.json").read_text() + ) + return cast(dict[str, str], pkg["devDependencies"]) def make_source_to_test(passing: bool = True): @@ -170,39 +143,23 @@ def test_jest_tests_are_successful( test_script: dict[str, str], package_json_target: str, passing: bool, - jest_lockfile: dict[str, str], + package_manager: str, jest_dev_dependencies: dict[str, str], ) -> None: - rule_runner.write_files( - { - "foo/BUILD": package_json_target, - "foo/package.json": given_package_json( - test_script=test_script, - dev_dependencies=jest_dev_dependencies, - ), - **{f"foo/{key}": value for key, value in jest_lockfile.items()}, - "foo/src/BUILD": "javascript_sources()", - "foo/src/index.mjs": make_source_to_test(passing), - "foo/src/tests/BUILD": "javascript_tests(name='tests')", - "foo/src/tests/index.test.js": textwrap.dedent( - """\ - /** - * @jest-environment node - */ - - import { expect } from "@jest/globals" + test_files = load_js_test_project("jest_project", package_manager=package_manager) + test_files["jest_project/BUILD"] = package_json_target + test_files["jest_project/package.json"] = given_package_json( + test_script=test_script, + dev_dependencies=jest_dev_dependencies, + ) + test_files["jest_project/src/index.mjs"] = make_source_to_test(passing) - import { add } from "../index.mjs" + rule_runner.write_files(test_files) - test('adds 1 + 2 to equal 3', () => { - expect(add(1, 2)).toBe(3); - }); - """ - ), - } + tgt = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="index.test.js") ) - tgt = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.js")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + package = rule_runner.get_target(Address("jest_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt, package=package)]) if passing: assert b"Test Suites: 1 passed, 1 total" in result.stderr_bytes @@ -214,55 +171,37 @@ def test_jest_tests_are_successful( def test_batched_jest_tests_are_successful( rule_runner: RuleRunner, - jest_lockfile: dict[str, str], - jest_dev_dependencies: dict[str, str], + package_manager: str, ) -> None: - rule_runner.write_files( - { - "foo/BUILD": "package_json()", - "foo/package.json": given_package_json( - test_script={"test": "NODE_OPTIONS=--experimental-vm-modules jest"}, - dev_dependencies=jest_dev_dependencies, - ), - **{f"foo/{key}": value for key, value in jest_lockfile.items()}, - "foo/src/BUILD": "javascript_sources()", - "foo/src/index.mjs": make_source_to_test(), - "foo/src/tests/BUILD": "javascript_tests(name='tests', batch_compatibility_tag='default')", - "foo/src/tests/index.test.js": textwrap.dedent( - """\ - /** - * @jest-environment node - */ - - import { expect } from "@jest/globals" + test_files = load_js_test_project("jest_project", package_manager=package_manager) + test_files["jest_project/src/tests/BUILD"] = ( + "javascript_tests(name='tests', batch_compatibility_tag='default')" + ) + test_files["jest_project/src/tests/another.test.js"] = textwrap.dedent( + """\ + /** + * @jest-environment node + */ - import { add } from "../index.mjs" + import { expect } from "@jest/globals" - test('adds 1 + 2 to equal 3', () => { - expect(add(1, 2)).toBe(3); - }); - """ - ), - "foo/src/tests/another.test.js": textwrap.dedent( - """\ - /** - * @jest-environment node - */ + import { add } from "../index.mjs" - import { expect } from "@jest/globals" + test('adds 2 + 3 to equal 5', () => { + expect(add(2, 3)).toBe(5); + }); + """ + ) - import { add } from "../index.mjs" + rule_runner.write_files(test_files) - test('adds 2 + 3 to equal 5', () => { - expect(add(2, 3)).toBe(5); - }); - """ - ), - } + tgt_1 = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="index.test.js") + ) + tgt_2 = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="another.test.js") ) - tgt_1 = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.js")) - tgt_2 = rule_runner.get_target(Address("foo/src/tests", relative_file_path="another.test.js")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + package = rule_runner.get_target(Address("jest_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt_1, tgt_2, package=package)]) assert b"Test Suites: 2 passed, 2 total" in result.stderr_bytes assert result.exit_code == 0 @@ -270,42 +209,44 @@ def test_batched_jest_tests_are_successful( def test_jest_tests_import_typescript_file( rule_runner: RuleRunner, - jest_lockfile: dict[str, str], + package_manager: str, jest_dev_dependencies: dict[str, str], ) -> None: - rule_runner.write_files( - { - "foo/BUILD": "package_json()", - "foo/package.json": given_package_json( - test_script={"test": "NODE_OPTIONS=--experimental-vm-modules jest"}, - dev_dependencies=jest_dev_dependencies, - jest={ - "extensionsToTreatAsEsm": [".ts"], - }, - ), - **{f"foo/{key}": value for key, value in jest_lockfile.items()}, - "foo/src/BUILD": "typescript_sources()", - "foo/src/index.ts": make_source_to_test(), - "foo/src/tests/BUILD": "javascript_tests(name='tests', batch_compatibility_tag='default')", - "foo/src/tests/index.test.js": textwrap.dedent( - """\ - /** - * @jest-environment node - */ + test_files = load_js_test_project("jest_project", package_manager=package_manager) + test_files["jest_project/package.json"] = given_package_json( + test_script={"test": "NODE_OPTIONS=--experimental-vm-modules jest"}, + dev_dependencies=jest_dev_dependencies, + jest={ + "extensionsToTreatAsEsm": [".ts"], + }, + ) + test_files["jest_project/src/BUILD"] = "typescript_sources()" + test_files["jest_project/src/index.ts"] = make_source_to_test() + test_files["jest_project/src/tests/BUILD"] = ( + "javascript_tests(name='tests', batch_compatibility_tag='default')" + ) + test_files["jest_project/src/tests/index.test.js"] = textwrap.dedent( + """\ + /** + * @jest-environment node + */ - import { expect } from "@jest/globals" + import { expect } from "@jest/globals" - import { add } from "../index.ts" + import { add } from "../index.ts" - test('adds 1 + 2 to equal 3', () => { - expect(add(1, 2)).toBe(3); - }); - """ - ), - } + test('adds 1 + 2 to equal 3', () => { + expect(add(1, 2)).toBe(3); + }); + """ + ) + + rule_runner.write_files(test_files) + + tgt_1 = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="index.test.js") ) - tgt_1 = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.js")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + package = rule_runner.get_target(Address("jest_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt_1, package=package)]) assert b"Test Suites: 1 passed, 1 total" in result.stderr_bytes assert result.exit_code == 0 @@ -314,36 +255,18 @@ def test_jest_tests_import_typescript_file( @pytest.mark.parametrize("passing", [True, False]) def test_mocha_tests( passing: bool, - mocha_lockfile: dict[str, str], - mocha_dev_dependencies: dict[str, str], + package_manager: str, rule_runner: RuleRunner, ) -> None: - rule_runner.write_files( - { - "foo/BUILD": "package_json()", - "foo/package.json": given_package_json( - test_script={"test": "mocha"}, - dev_dependencies=mocha_dev_dependencies, - ), - **{f"foo/{key}": value for key, value in mocha_lockfile.items()}, - "foo/src/BUILD": "javascript_sources()", - "foo/src/index.mjs": make_source_to_test(passing), - "foo/src/tests/BUILD": "javascript_tests(name='tests')", - "foo/src/tests/index.test.mjs": textwrap.dedent( - """\ - import assert from "assert" + test_files = load_js_test_project("mocha_project", package_manager=package_manager) + test_files["mocha_project/src/index.mjs"] = make_source_to_test(passing) - import { add } from "../index.mjs" + rule_runner.write_files(test_files) - it('adds 1 + 2 to equal 3', () => { - assert.equal(add(1, 2), 3); - }); - """ - ), - } + tgt = rule_runner.get_target( + Address("mocha_project/src/tests", relative_file_path="index.test.mjs") ) - tgt = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.mjs")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + package = rule_runner.get_target(Address("mocha_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt, package=package)]) if passing: assert b"1 passing" in result.stdout_bytes @@ -356,54 +279,32 @@ def test_mocha_tests( def test_jest_test_with_coverage_reporting( package_manager: str, rule_runner: RuleRunner, - jest_lockfile: dict[str, str], - jest_dev_dependencies: dict[str, str], ) -> None: rule_runner.set_options( args=[f"--nodejs-package-manager={package_manager}", "--test-use-coverage", "True"], env_inherit={"PATH"}, ) - rule_runner.write_files( - { - "foo/BUILD": textwrap.dedent( - """\ - package_json( - scripts=[ - node_test_script( - coverage_args=['--coverage', '--coverage-directory=.coverage/'], - coverage_output_files=['.coverage/clover.xml'], - ) - ], - ) - """ - ), - "foo/package.json": given_package_json( - test_script={"test": "NODE_OPTIONS=--experimental-vm-modules jest"}, - dev_dependencies=jest_dev_dependencies, - ), - **{f"foo/{key}": value for key, value in jest_lockfile.items()}, - "foo/src/BUILD": "javascript_sources()", - "foo/src/index.mjs": make_source_to_test(), - "foo/src/tests/BUILD": "javascript_tests(name='tests')", - "foo/src/tests/index.test.js": textwrap.dedent( - """\ - /** - * @jest-environment node - */ - import { expect } from "@jest/globals" + test_files = load_js_test_project("jest_project", package_manager=package_manager) + test_files["jest_project/BUILD"] = textwrap.dedent( + """\ + package_json( + scripts=[ + node_test_script( + coverage_args=['--coverage', '--coverage-directory=.coverage/'], + coverage_output_files=['.coverage/clover.xml'], + ) + ], + ) + """ + ) - import { add } from "../index.mjs" + rule_runner.write_files(test_files) - test('adds 1 + 2 to equal 3', () => { - expect(add(1, 2)).toBe(3); - }); - """ - ), - } + tgt = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="index.test.js") ) - tgt = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.js")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + package = rule_runner.get_target(Address("jest_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt, package=package)]) assert result.coverage_data @@ -421,43 +322,42 @@ def given_request_for(*js_test: Target, package: Target) -> JSTestRequest.Batch: def test_typescript_test_files( rule_runner: RuleRunner, - jest_lockfile: dict[str, str], + package_manager: str, jest_dev_dependencies: dict[str, str], ) -> None: - """Test that TypeScript test files are recognized and executed.""" - rule_runner.write_files( - { - "foo/BUILD": "package_json()", - "foo/package.json": given_package_json( - test_script={"test": "jest"}, - dev_dependencies=jest_dev_dependencies, - jest={ - "preset": "ts-jest", - }, - ), - **{f"foo/{key}": value for key, value in jest_lockfile.items()}, - "foo/src/BUILD": "typescript_sources()", - "foo/src/index.ts": textwrap.dedent( - """\ - export function add(x: number, y: number): number { - return x + y; - } - """ - ), - "foo/src/tests/BUILD": "typescript_tests(name='tests')", - "foo/src/tests/index.test.ts": textwrap.dedent( - """\ - import { add } from '../index'; - - test('adds 1 + 2 to equal 3', () => { - expect(add(1, 2)).toBe(3); - }); - """ - ), + test_files = load_js_test_project("jest_project", package_manager=package_manager) + test_files["jest_project/package.json"] = given_package_json( + test_script={"test": "jest"}, + dev_dependencies=jest_dev_dependencies, + jest={ + "preset": "ts-jest", + }, + ) + test_files["jest_project/src/BUILD"] = "typescript_sources()" + test_files["jest_project/src/index.ts"] = textwrap.dedent( + """\ + export function add(x: number, y: number): number { + return x + y; } + """ + ) + test_files["jest_project/src/tests/BUILD"] = "typescript_tests(name='tests')" + test_files["jest_project/src/tests/index.test.ts"] = textwrap.dedent( + """\ + import { add } from '../index'; + + test('adds 1 + 2 to equal 3', () => { + expect(add(1, 2)).toBe(3); + }); + """ ) - tgt = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.ts")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + + rule_runner.write_files(test_files) + + tgt = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="index.test.ts") + ) + package = rule_runner.get_target(Address("jest_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt, package=package)]) assert b"Test Suites: 1 passed, 1 total" in result.stderr_bytes assert result.exit_code == 0 @@ -465,42 +365,41 @@ def test_typescript_test_files( def test_tsx_test_files( rule_runner: RuleRunner, - jest_lockfile: dict[str, str], + package_manager: str, jest_dev_dependencies: dict[str, str], ) -> None: - """Test that TSX test files are recognized and executed.""" - rule_runner.write_files( - { - "foo/BUILD": "package_json()", - "foo/package.json": given_package_json( - test_script={"test": "jest"}, - dev_dependencies=jest_dev_dependencies, - jest={"preset": "ts-jest", "globals": {"ts-jest": {"tsconfig": {"jsx": "react"}}}}, - ), - **{f"foo/{key}": value for key, value in jest_lockfile.items()}, - "foo/tsconfig.json": json.dumps({"compilerOptions": {"jsx": "react"}}), - "foo/src/BUILD": "tsx_sources()", - "foo/src/index.tsx": textwrap.dedent( - """\ - export function add(x: number, y: number): number { - return x + y; - } - """ - ), - "foo/src/tests/BUILD": "tsx_tests(name='tests')", - "foo/src/tests/index.test.tsx": textwrap.dedent( - """\ - import { add } from '../index'; - - test('adds 1 + 2 to equal 3', () => { - expect(add(1, 2)).toBe(3); - }); - """ - ), + test_files = load_js_test_project("jest_project", package_manager=package_manager) + test_files["jest_project/package.json"] = given_package_json( + test_script={"test": "jest"}, + dev_dependencies=jest_dev_dependencies, + jest={"preset": "ts-jest", "globals": {"ts-jest": {"tsconfig": {"jsx": "react"}}}}, + ) + test_files["jest_project/tsconfig.json"] = json.dumps({"compilerOptions": {"jsx": "react"}}) + test_files["jest_project/src/BUILD"] = "tsx_sources()" + test_files["jest_project/src/index.tsx"] = textwrap.dedent( + """\ + export function add(x: number, y: number): number { + return x + y; } + """ + ) + test_files["jest_project/src/tests/BUILD"] = "tsx_tests(name='tests')" + test_files["jest_project/src/tests/index.test.tsx"] = textwrap.dedent( + """\ + import { add } from '../index'; + + test('adds 1 + 2 to equal 3', () => { + expect(add(1, 2)).toBe(3); + }); + """ + ) + + rule_runner.write_files(test_files) + + tgt = rule_runner.get_target( + Address("jest_project/src/tests", relative_file_path="index.test.tsx") ) - tgt = rule_runner.get_target(Address("foo/src/tests", relative_file_path="index.test.tsx")) - package = rule_runner.get_target(Address("foo", generated_name="pkg")) + package = rule_runner.get_target(Address("jest_project", generated_name="pkg")) result = rule_runner.request(TestResult, [given_request_for(tgt, package=package)]) assert b"Test Suites: 1 passed, 1 total" in result.stderr_bytes assert result.exit_code == 0 diff --git a/src/python/pants/backend/javascript/goals/test_resources/.gitignore b/src/python/pants/backend/javascript/goals/test_resources/.gitignore new file mode 100644 index 00000000000..380f475bbcf --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.pnpm-store/ diff --git a/src/python/pants/backend/javascript/goals/test_resources/README.md b/src/python/pants/backend/javascript/goals/test_resources/README.md new file mode 100644 index 00000000000..1745723ff9c --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/README.md @@ -0,0 +1,48 @@ +# JavaScript Backend Test Resources + +This directory contains test project fixtures for the JavaScript backend tests. + +## Structure + +Each subdirectory represents a complete, working JavaScript project that can be used by tests: + +- `jest_project/` - Complete jest test project with source and test files +- `mocha_project/` - Complete mocha test project with source and test files + +## Usage + +Use `load_js_test_project()` from `pants.backend.javascript.goals.testutil` to load test resources: + +```python +from pants.backend.javascript.goals.testutil import load_js_test_project + +# Load all files for a specific package manager +test_files = load_js_test_project("jest_project", package_manager="npm") + +# Override specific files when needed +test_files["jest_project/src/index.mjs"] = "export function add(x, y) { return x - y }" + +rule_runner.write_files(test_files) +``` + +## Package Manager Support + +Projects include lockfiles for all supported package managers (npm, pnpm, yarn). +Tests select the appropriate lockfile based on the `--nodejs-package-manager` option. + +## Regenerating Lockfiles + +To update lockfiles after modifying package.json: + +```bash +cd jest_project + +# npm +npm install --package-lock-only + +# pnpm +pnpm install --lockfile-only + +# yarn +yarn install --mode update-lockfile +``` diff --git a/src/python/pants/backend/javascript/goals/test_resources/jest_project/BUILD b/src/python/pants/backend/javascript/goals/test_resources/jest_project/BUILD new file mode 100644 index 00000000000..5f1d050e03b --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/jest_project/BUILD @@ -0,0 +1,3 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +package_json() diff --git a/src/python/pants/backend/javascript/goals/jest_resources/package-lock.json b/src/python/pants/backend/javascript/goals/test_resources/jest_project/package-lock.json similarity index 100% rename from src/python/pants/backend/javascript/goals/jest_resources/package-lock.json rename to src/python/pants/backend/javascript/goals/test_resources/jest_project/package-lock.json diff --git a/src/python/pants/backend/javascript/goals/jest_resources/package.json b/src/python/pants/backend/javascript/goals/test_resources/jest_project/package.json similarity index 73% rename from src/python/pants/backend/javascript/goals/jest_resources/package.json rename to src/python/pants/backend/javascript/goals/test_resources/jest_project/package.json index 4899743eab6..767c5039ca9 100644 --- a/src/python/pants/backend/javascript/goals/jest_resources/package.json +++ b/src/python/pants/backend/javascript/goals/test_resources/jest_project/package.json @@ -3,6 +3,9 @@ "version": "0.0.1", "type": "module", "main": "./src/index.mjs", + "scripts": { + "test": "NODE_OPTIONS=--experimental-vm-modules jest" + }, "devDependencies": { "@types/jest": "30.0.0", "jest": "30.2.0", diff --git a/src/python/pants/backend/javascript/goals/jest_resources/pnpm-lock.yaml b/src/python/pants/backend/javascript/goals/test_resources/jest_project/pnpm-lock.yaml similarity index 100% rename from src/python/pants/backend/javascript/goals/jest_resources/pnpm-lock.yaml rename to src/python/pants/backend/javascript/goals/test_resources/jest_project/pnpm-lock.yaml diff --git a/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/BUILD b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/BUILD new file mode 100644 index 00000000000..674c8b3295d --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/BUILD @@ -0,0 +1,3 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +javascript_sources() diff --git a/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/index.mjs b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/index.mjs new file mode 100644 index 00000000000..f61f7f2c7d6 --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/index.mjs @@ -0,0 +1,5 @@ +// Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +// Licensed under the Apache License, Version 2.0 (see LICENSE). +export function add(x, y) { + return x + y; +} diff --git a/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/tests/BUILD b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/tests/BUILD new file mode 100644 index 00000000000..f10d5f00e73 --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/tests/BUILD @@ -0,0 +1,3 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +javascript_tests(name="tests") diff --git a/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/tests/index.test.js b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/tests/index.test.js new file mode 100644 index 00000000000..3bbb433d9d1 --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/jest_project/src/tests/index.test.js @@ -0,0 +1,13 @@ +// Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +// Licensed under the Apache License, Version 2.0 (see LICENSE). +/** + * @jest-environment node + */ + +import { expect } from "@jest/globals"; + +import { add } from "../index.mjs"; + +test("adds 1 + 2 to equal 3", () => { + expect(add(1, 2)).toBe(3); +}); diff --git a/src/python/pants/backend/javascript/goals/jest_resources/yarn.lock b/src/python/pants/backend/javascript/goals/test_resources/jest_project/yarn.lock similarity index 100% rename from src/python/pants/backend/javascript/goals/jest_resources/yarn.lock rename to src/python/pants/backend/javascript/goals/test_resources/jest_project/yarn.lock diff --git a/src/python/pants/backend/javascript/goals/test_resources/mocha_project/BUILD b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/BUILD new file mode 100644 index 00000000000..5f1d050e03b --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/BUILD @@ -0,0 +1,3 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +package_json() diff --git a/src/python/pants/backend/javascript/goals/mocha_resources/package-lock.json b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/package-lock.json similarity index 100% rename from src/python/pants/backend/javascript/goals/mocha_resources/package-lock.json rename to src/python/pants/backend/javascript/goals/test_resources/mocha_project/package-lock.json diff --git a/src/python/pants/backend/javascript/goals/mocha_resources/package.json b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/package.json similarity index 77% rename from src/python/pants/backend/javascript/goals/mocha_resources/package.json rename to src/python/pants/backend/javascript/goals/test_resources/mocha_project/package.json index c001bc3963b..6ccae7323fc 100644 --- a/src/python/pants/backend/javascript/goals/mocha_resources/package.json +++ b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/package.json @@ -3,6 +3,9 @@ "version": "0.0.1", "type": "module", "main": "./src/index.mjs", + "scripts": { + "test": "mocha" + }, "devDependencies": { "mocha": "11.7.4" } diff --git a/src/python/pants/backend/javascript/goals/mocha_resources/pnpm-lock.yaml b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/pnpm-lock.yaml similarity index 100% rename from src/python/pants/backend/javascript/goals/mocha_resources/pnpm-lock.yaml rename to src/python/pants/backend/javascript/goals/test_resources/mocha_project/pnpm-lock.yaml diff --git a/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/BUILD b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/BUILD new file mode 100644 index 00000000000..674c8b3295d --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/BUILD @@ -0,0 +1,3 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +javascript_sources() diff --git a/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/index.mjs b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/index.mjs new file mode 100644 index 00000000000..f61f7f2c7d6 --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/index.mjs @@ -0,0 +1,5 @@ +// Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +// Licensed under the Apache License, Version 2.0 (see LICENSE). +export function add(x, y) { + return x + y; +} diff --git a/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/tests/BUILD b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/tests/BUILD new file mode 100644 index 00000000000..f10d5f00e73 --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/tests/BUILD @@ -0,0 +1,3 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +javascript_tests(name="tests") diff --git a/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/tests/index.test.mjs b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/tests/index.test.mjs new file mode 100644 index 00000000000..6b7649c5674 --- /dev/null +++ b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/src/tests/index.test.mjs @@ -0,0 +1,9 @@ +// Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +// Licensed under the Apache License, Version 2.0 (see LICENSE). +import assert from "assert"; + +import { add } from "../index.mjs"; + +it("adds 1 + 2 to equal 3", () => { + assert.equal(add(1, 2), 3); +}); diff --git a/src/python/pants/backend/javascript/goals/mocha_resources/yarn.lock b/src/python/pants/backend/javascript/goals/test_resources/mocha_project/yarn.lock similarity index 100% rename from src/python/pants/backend/javascript/goals/mocha_resources/yarn.lock rename to src/python/pants/backend/javascript/goals/test_resources/mocha_project/yarn.lock diff --git a/src/python/pants/backend/javascript/goals/testutil.py b/src/python/pants/backend/javascript/goals/testutil.py new file mode 100644 index 00000000000..cef081fbf8b --- /dev/null +++ b/src/python/pants/backend/javascript/goals/testutil.py @@ -0,0 +1,36 @@ +# Copyright 2026 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +from pathlib import Path + +LOCKFILE_NAMES = { + "npm": "package-lock.json", + "pnpm": "pnpm-lock.yaml", + "yarn": "yarn.lock", +} + + +def load_js_test_project( + project_name: str, *, package_manager: str | None = None +) -> dict[str, str]: + base_dir = Path(__file__).parent / "test_resources" / project_name + files = {} + + # Determine which lockfiles to exclude + exclude_lockfiles = set() + if package_manager: + for pm, lockfile in LOCKFILE_NAMES.items(): + if pm != package_manager: + exclude_lockfiles.add(lockfile) + + for file_path in base_dir.rglob("*"): + if file_path.is_file(): + # Skip lockfiles not for the selected package manager + if file_path.name in exclude_lockfiles: + continue + relative_path = file_path.relative_to(base_dir) + files[f"{project_name}/{relative_path}"] = file_path.read_text() + + return files