• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 18813461704

26 Oct 2025 05:18AM UTC coverage: 80.277% (-0.002%) from 80.279%
18813461704

Pull #22806

github

web-flow
Merge edfaeed41 into 4834308dc
Pull Request #22806: Updated Javascript dependencies and re-locked

6 of 6 new or added lines in 5 files covered. (100.0%)

2 existing lines in 1 file now uncovered.

77869 of 97000 relevant lines covered (80.28%)

3.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.37
/src/python/pants/backend/javascript/goals/lockfile_test.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
1✔
4

5
import json
1✔
6
import textwrap
1✔
7

8
import pytest
1✔
9
import yaml
1✔
10

11
from pants.backend.javascript.goals import lockfile
1✔
12
from pants.backend.javascript.goals.lockfile import (
1✔
13
    GeneratePackageLockJsonFile,
14
    KnownPackageJsonUserResolveNamesRequest,
15
    RequestedPackageJsonUserResolveNames,
16
)
17
from pants.backend.javascript.nodejs_project import AllNodeJSProjects
1✔
18
from pants.backend.javascript.package_json import (
1✔
19
    AllPackageJson,
20
    PackageJsonForGlobs,
21
    PackageJsonTarget,
22
)
23
from pants.backend.javascript.subsystems.nodejs import UserChosenNodeJSResolveAliases
1✔
24
from pants.core.goals.generate_lockfiles import (
1✔
25
    GenerateLockfileResult,
26
    KnownUserResolveNames,
27
    UserGenerateLockfiles,
28
)
29
from pants.core.target_types import FileTarget
1✔
30
from pants.engine.fs import DigestContents, PathGlobs
1✔
31
from pants.engine.internals.scheduler import ExecutionError
1✔
32
from pants.engine.rules import QueryRule
1✔
33
from pants.testutil.rule_runner import RuleRunner
1✔
34

35

36
@pytest.fixture
1✔
37
def rule_runner() -> RuleRunner:
1✔
38
    rule_runner = RuleRunner(
1✔
39
        rules=[
40
            *lockfile.rules(),
41
            QueryRule(
42
                KnownUserResolveNames, (KnownPackageJsonUserResolveNamesRequest, AllNodeJSProjects)
43
            ),
44
            QueryRule(AllNodeJSProjects, ()),
45
            QueryRule(PackageJsonForGlobs, (PathGlobs,)),
46
            QueryRule(AllPackageJson, (PathGlobs,)),
47
            QueryRule(GenerateLockfileResult, (GeneratePackageLockJsonFile,)),
48
            QueryRule(
49
                UserGenerateLockfiles,
50
                (
51
                    RequestedPackageJsonUserResolveNames,
52
                    AllNodeJSProjects,
53
                    UserChosenNodeJSResolveAliases,
54
                ),
55
            ),
56
        ],
57
        target_types=[PackageJsonTarget, FileTarget],
58
    )
59
    rule_runner.set_options([], env_inherit={"PATH"})
1✔
60
    return rule_runner
1✔
61

62

63
def given_package_with_name(name: str) -> str:
1✔
64
    return json.dumps({"name": name, "version": "0.0.1"})
1✔
65

66

67
def given_package_with_workspaces(
1✔
68
    name: str,
69
    version: str,
70
    dependencies: dict[str, str] | None = None,
71
    *workspaces: str,
72
) -> str:
73
    return json.dumps(
1✔
74
        {
75
            "name": name,
76
            "version": version,
77
            "private": True,
78
            "dependencies": dependencies or {},
79
            "workspaces": list(workspaces),
80
        }
81
    )
82

83

84
def test_resolves_are_dotted_package_paths(rule_runner: RuleRunner) -> None:
1✔
85
    rule_runner.write_files(
1✔
86
        {
87
            "src/js/foo/BUILD": "package_json()",
88
            "src/js/foo/package.json": given_package_with_name("ham"),
89
            "src/js/bar/BUILD": "package_json()",
90
            "src/js/bar/package.json": given_package_with_name("spam"),
91
        }
92
    )
93
    projects = rule_runner.request(AllNodeJSProjects, [])
1✔
94
    resolves = rule_runner.request(
1✔
95
        KnownUserResolveNames, (projects, KnownPackageJsonUserResolveNamesRequest())
96
    )
97
    assert set(resolves.names) == {"js.foo", "js.bar"}
1✔
98

99

100
def test_user_can_override_resolve_aliases(rule_runner: RuleRunner) -> None:
1✔
101
    rule_runner.write_files(
1✔
102
        {
103
            "src/js/foo/BUILD": "package_json()",
104
            "src/js/foo/package.json": given_package_with_name("ham"),
105
            "src/js/bar/BUILD": "package_json()",
106
            "src/js/bar/package.json": given_package_with_name("spam"),
107
        }
108
    )
109
    projects = rule_runner.request(AllNodeJSProjects, [])
1✔
110
    rule_runner.set_options(["--nodejs-resolves={'user:1': 'src/js/foo/package-lock.json'}"])
1✔
111
    resolves = rule_runner.request(
1✔
112
        KnownUserResolveNames, (projects, KnownPackageJsonUserResolveNamesRequest())
113
    )
114
    assert set(resolves.names) == {"user:1", "js.bar"}
1✔
115

116

117
def test_user_override_non_existing_resolve_is_an_error(rule_runner: RuleRunner) -> None:
1✔
118
    rule_runner.write_files(
1✔
119
        {
120
            "src/js/foo/BUILD": "package_json()",
121
            "src/js/foo/package.json": given_package_with_name("ham"),
122
        }
123
    )
124
    projects = rule_runner.request(AllNodeJSProjects, [])
1✔
125
    rule_runner.set_options(["--nodejs-resolves={'user:1': 'does/not/exist/package-lock.json'}"])
1✔
126
    with pytest.raises(ExecutionError):
1✔
127
        rule_runner.request(
1✔
128
            KnownUserResolveNames, (projects, KnownPackageJsonUserResolveNamesRequest())
129
        )
130

131

132
@pytest.mark.parametrize(
1✔
133
    "alias_args, expected_resolve",
134
    [
135
        pytest.param(
136
            ["--nodejs-resolves={'my-resolve': 'src/js/package-lock.json'}"],
137
            "my-resolve",
138
            id="Aliased resolve",
139
        ),
140
        pytest.param([""], "js", id="Default resolve"),
141
    ],
142
)
143
def test_generates_lockfile_with_expected_resolve_name(
1✔
144
    rule_runner: RuleRunner, alias_args: list[str], expected_resolve: str
145
) -> None:
146
    rule_runner.write_files(
1✔
147
        {
148
            "src/js/BUILD": "package_json()",
149
            "src/js/package.json": given_package_with_name("ham"),
150
        }
151
    )
152
    projects = rule_runner.request(AllNodeJSProjects, [])
1✔
153
    rule_runner.set_options(alias_args)
1✔
154
    [lockfile] = rule_runner.request(
1✔
155
        UserGenerateLockfiles,
156
        (
157
            projects,
158
            RequestedPackageJsonUserResolveNames((expected_resolve,)),
159
            UserChosenNodeJSResolveAliases(),
160
        ),
161
    )
162
    assert lockfile.resolve_name == expected_resolve
1✔
163

164

165
def test_generates_lockfile_for_package_json_project(rule_runner: RuleRunner) -> None:
1✔
166
    rule_runner.write_files(
1✔
167
        {
168
            "src/js/BUILD": "package_json()",
169
            "src/js/package.json": given_package_with_name("ham"),
170
        }
171
    )
172
    [project] = rule_runner.request(AllNodeJSProjects, [])
1✔
173

174
    lockfile = rule_runner.request(
1✔
175
        GenerateLockfileResult,
176
        (
177
            GeneratePackageLockJsonFile(
178
                resolve_name="js",
179
                lockfile_dest="src/js/package-lock.json",
180
                project=project,
181
                diff=False,
182
            ),
183
        ),
184
    )
185

186
    digest_contents = rule_runner.request(DigestContents, [lockfile.digest])
1✔
187

188
    assert json.loads(digest_contents[0].content) == {
1✔
189
        "name": "ham",
190
        "version": "0.0.1",
191
        "lockfileVersion": 3,
192
        "requires": True,
193
        "packages": {"": {"name": "ham", "version": "0.0.1"}},
194
    }
195

196

197
def test_generates_lockfile_for_npm_package_json_workspace(rule_runner: RuleRunner) -> None:
1✔
198
    rule_runner.set_options(["--nodejs-package-manager=npm"], env_inherit={"PATH"})
1✔
199
    rule_runner.write_files(
1✔
200
        {
201
            "src/js/BUILD": "package_json()",
202
            "src/js/package.json": given_package_with_workspaces("ham", "1.0.0", None, "a"),
203
            "src/js/a/BUILD": "package_json()",
204
            "src/js/a/package.json": given_package_with_workspaces(
205
                "spam",
206
                "0.1.0",
207
            ),
208
        }
209
    )
210
    [project] = rule_runner.request(AllNodeJSProjects, [])
1✔
211

212
    lockfile = rule_runner.request(
1✔
213
        GenerateLockfileResult,
214
        (
215
            GeneratePackageLockJsonFile(
216
                resolve_name="js",
217
                lockfile_dest="src/js/package-lock.json",
218
                project=project,
219
                diff=False,
220
            ),
221
        ),
222
    )
223

224
    digest_contents = rule_runner.request(DigestContents, [lockfile.digest])
1✔
225

226
    assert json.loads(digest_contents[0].content) == {
1✔
227
        "name": "ham",
228
        "version": "1.0.0",
229
        "lockfileVersion": 3,
230
        "requires": True,
231
        "packages": {
232
            "": {"name": "ham", "version": "1.0.0", "workspaces": ["a"]},
233
            "a": {"name": "spam", "version": "0.1.0"},
234
            "node_modules/spam": {"link": True, "resolved": "a"},
235
        },
236
    }
237

238

239
def test_generates_lockfile_for_pnpm_package_json_workspace(rule_runner: RuleRunner) -> None:
1✔
240
    rule_runner.set_options(["--nodejs-package-manager=pnpm"], env_inherit={"PATH"})
1✔
241
    rule_runner.write_files(
1✔
242
        {
243
            "src/js/BUILD": "package_json()",
244
            "src/js/pnpm-workspace.yaml": "",
245
            "src/js/package.json": given_package_with_workspaces(
246
                "ham", "1.0.0", {"spam": "workspace:*"}
247
            ),
248
            "src/js/a/BUILD": "package_json()",
249
            "src/js/a/package.json": given_package_with_workspaces("spam", "0.1.0"),
250
        }
251
    )
252
    [project] = rule_runner.request(AllNodeJSProjects, [])
1✔
253

254
    lockfile = rule_runner.request(
1✔
255
        GenerateLockfileResult,
256
        (
257
            GeneratePackageLockJsonFile(
258
                resolve_name="js",
259
                lockfile_dest="src/js/pnpm-lock.yaml",
260
                project=project,
261
                diff=False,
262
            ),
263
        ),
264
    )
265

UNCOV
266
    digest_contents = rule_runner.request(DigestContents, [lockfile.digest])
×
267

UNCOV
268
    assert yaml.safe_load(digest_contents[0].content) == {
×
269
        "importers": {
270
            ".": {"dependencies": {"spam": {"specifier": "workspace:*", "version": "link:a"}}},
271
            "a": {},
272
        },
273
        "lockfileVersion": "9.0",
274
        "settings": {
275
            "autoInstallPeers": True,
276
            "excludeLinksFromLockfile": False,
277
        },
278
    }
279

280

281
def test_generates_lockfile_for_yarn_package_json_workspace(rule_runner: RuleRunner) -> None:
1✔
282
    rule_runner.set_options(["--nodejs-package-manager=yarn"], env_inherit={"PATH"})
1✔
283
    rule_runner.write_files(
1✔
284
        {
285
            "src/js/BUILD": "package_json()",
286
            "src/js/package.json": given_package_with_workspaces(
287
                "ham", "1.0.0", {"spam": "*"}, "a"
288
            ),
289
            "src/js/a/BUILD": "package_json()",
290
            "src/js/a/package.json": given_package_with_workspaces("spam", "0.1.0"),
291
        }
292
    )
293
    [project] = rule_runner.request(AllNodeJSProjects, [])
1✔
294

295
    lockfile = rule_runner.request(
1✔
296
        GenerateLockfileResult,
297
        (
298
            GeneratePackageLockJsonFile(
299
                resolve_name="js",
300
                lockfile_dest="src/js/yarn.lock",
301
                project=project,
302
                diff=False,
303
            ),
304
        ),
305
    )
306

307
    digest_contents = rule_runner.request(DigestContents, [lockfile.digest])
1✔
308

309
    assert (
1✔
310
        digest_contents[0].content.decode().strip()
311
        == textwrap.dedent(
312
            """\
313
        # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
314
        # yarn lockfile v1
315
        """
316
        ).strip()
317
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc