Skip to content

Commit

Permalink
feat(npm-dist-tag): Use fetch API instead of CLI to make changes
Browse files Browse the repository at this point in the history
  • Loading branch information
evocateur committed Dec 7, 2018
1 parent b387881 commit 54008c6
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 151 deletions.
4 changes: 2 additions & 2 deletions commands/__mocks__/@lerna/npm-dist-tag.js
@@ -1,9 +1,9 @@
"use strict";

const mockAdd = jest.fn(() => Promise.resolve());
const mockCheck = jest.fn(() => true);
const mockList = jest.fn(() => Promise.resolve({}));
const mockRemove = jest.fn(() => Promise.resolve());

exports.add = mockAdd;
exports.check = mockCheck;
exports.list = mockList;
exports.remove = mockRemove;
1 change: 0 additions & 1 deletion commands/publish/__tests__/publish-command.test.js
Expand Up @@ -108,7 +108,6 @@ Set {
"package-2",
// package-5 is private
]);
expect(npmDistTag.check).not.toHaveBeenCalled();
expect(npmDistTag.remove).not.toHaveBeenCalled();
expect(npmDistTag.add).not.toHaveBeenCalled();

Expand Down
23 changes: 12 additions & 11 deletions commands/publish/__tests__/publish-tagging.test.js
Expand Up @@ -29,7 +29,7 @@ test("publish --npm-tag", async () => {
await lernaPublish(cwd)("--npm-tag", "custom");

expect(npmPublish.registry.get("package-3")).toBe("custom");
expect(npmDistTag.check).not.toHaveBeenCalled();
expect(npmDistTag.remove).not.toHaveBeenCalled();
});

test("publish --temp-tag", async () => {
Expand All @@ -41,14 +41,15 @@ test("publish --temp-tag", async () => {

expect(npmPublish.registry.get("package-4")).toBe("lerna-temp");

expect(npmDistTag.remove).toHaveBeenLastCalledWith(
expect.objectContaining({ name: "package-4" }),
"lerna-temp",
expect.objectContaining({ registry: "test-registry" })
);
expect(npmDistTag.add).toHaveBeenLastCalledWith(
expect.objectContaining({ name: "package-4", version: "1.0.1" }),
"latest",
expect.objectContaining({ registry: "test-registry" })
);
const conf = expect.objectContaining({
sources: expect.objectContaining({
cli: {
data: expect.objectContaining({
registry: "test-registry",
}),
},
}),
});
expect(npmDistTag.remove).toHaveBeenLastCalledWith("package-4@1.0.1", "lerna-temp", conf);
expect(npmDistTag.add).toHaveBeenLastCalledWith("package-4@1.0.1", "latest", conf);
});
18 changes: 8 additions & 10 deletions commands/publish/index.js
Expand Up @@ -576,16 +576,14 @@ class PublishCommand extends Command {
let chain = Promise.resolve();

const actions = [
pkg =>
Promise.resolve()
.then(() => npmDistTag.check(pkg, "lerna-temp", this.npmConfig))
.then(exists => {
if (exists) {
return npmDistTag.remove(pkg, "lerna-temp", this.npmConfig);
}
})
.then(() => npmDistTag.add(pkg, distTag, this.npmConfig))
.then(() => pkg),
pkg => {
const spec = `${pkg.name}@${pkg.version}`;

return Promise.resolve()
.then(() => npmDistTag.remove(spec, "lerna-temp", this.conf))
.then(() => npmDistTag.add(spec, distTag, this.conf))
.then(() => pkg);
},
pkg => {
tracker.info("dist-tag", "%s@%s => %j", pkg.name, pkg.version, distTag);
tracker.completeWork(1);
Expand Down
5 changes: 2 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

212 changes: 114 additions & 98 deletions utils/npm-dist-tag/__tests__/npm-dist-tag.test.js
@@ -1,122 +1,138 @@
"use strict";

jest.mock("@lerna/child-process");

const os = require("os");
jest.mock("libnpm/fetch");

// mocked modules
const ChildProcessUtilities = require("@lerna/child-process");
const fetch = require("libnpm/fetch");

// file under test
const npmDistTag = require("..");

describe("dist-tag", () => {
ChildProcessUtilities.exec.mockResolvedValue();

describe("npmDistTag.add()", () => {
const pkg = {
name: "foo-pkg",
version: "1.0.0",
location: "/test/npm/dist-tag/add",
};
const tag = "added-tag";
const registry = "https://custom-registry/add";

it("adds a dist-tag for a given package@version", async () => {
await npmDistTag.add(pkg, tag, {});

expect(ChildProcessUtilities.exec).toHaveBeenLastCalledWith(
"npm",
["dist-tag", "add", "foo-pkg@1.0.0", tag],
{
cwd: pkg.location,
env: {},
pkg,
}
);
});
const stubLog = {
verbose: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
};
const baseOptions = new Map([["log", stubLog], ["tag", "latest"]]);

fetch.mockImplementation(() => Promise.resolve());
fetch.json.mockImplementation(() => Promise.resolve({}));

describe("npmDistTag.add()", () => {
it("adds a dist-tag for a given package@version", async () => {
const opts = new Map(baseOptions);
const tags = await npmDistTag.add("@scope/some-pkg@1.0.1", "added-tag", opts);

it("supports custom registry", async () => {
await npmDistTag.add(pkg, tag, { registry });

expect(ChildProcessUtilities.exec).toHaveBeenLastCalledWith(
"npm",
["dist-tag", "add", "foo-pkg@1.0.0", tag],
{
cwd: pkg.location,
env: {
npm_config_registry: registry,
},
pkg,
}
);
expect(tags).toEqual({
"added-tag": "1.0.1",
});
expect(fetch).toHaveBeenLastCalledWith(
"-/package/@scope%2fsome-pkg/dist-tags/added-tag",
expect.objectContaining({
method: "PUT",
body: JSON.stringify("1.0.1"),
headers: {
"content-type": "application/json",
},
})
);
});

describe("npmDistTag.remove()", () => {
const pkg = {
name: "bar-pkg",
location: "/test/npm/dist-tag/remove",
};
const tag = "removed-tag";
const registry = "https://custom-registry/remove";

it("removes a dist-tag for a given package", async () => {
await npmDistTag.remove(pkg, tag, {});

expect(ChildProcessUtilities.exec).toHaveBeenLastCalledWith("npm", ["dist-tag", "rm", pkg.name, tag], {
cwd: pkg.location,
env: {},
pkg,
});
});
it("does not attempt to add duplicate of existing tag", async () => {
fetch.json.mockImplementationOnce(() =>
Promise.resolve({
latest: "1.0.0",
"dupe-tag": "1.0.1",
})
);

it("supports custom registry", async () => {
await npmDistTag.remove(pkg, tag, { registry });
const opts = new Map(baseOptions);
const tags = await npmDistTag.add("@scope/some-pkg@1.0.1", "dupe-tag", opts);

expect(ChildProcessUtilities.exec).toHaveBeenLastCalledWith("npm", ["dist-tag", "rm", pkg.name, tag], {
cwd: pkg.location,
env: {
npm_config_registry: registry,
},
pkg,
});
expect(tags).toEqual({
latest: "1.0.0",
"dupe-tag": "1.0.1",
});
expect(fetch).not.toHaveBeenCalled();
expect(stubLog.warn).toHaveBeenLastCalledWith(
"dist-tag",
"@scope/some-pkg@dupe-tag already set to 1.0.1"
);
});

describe("npmDistTag.check()", () => {
const pkg = {
name: "baz-pkg",
location: "/test/npm/dist-tag/check",
};
const registry = "https://custom-registry/check";

it("tests if a dist-tag for a given package exists", () => {
ChildProcessUtilities.execSync.mockReturnValue(["latest", "target-tag"].join(os.EOL));

expect(npmDistTag.check(pkg, "target-tag", {})).toBe(true);
expect(npmDistTag.check(pkg, "latest", {})).toBe(true);
expect(npmDistTag.check(pkg, "missing", {})).toBe(false);

expect(ChildProcessUtilities.execSync).toHaveBeenLastCalledWith("npm", ["dist-tag", "ls", pkg.name], {
cwd: pkg.location,
env: {},
pkg,
});
it("defaults tag argument to opts.tag", async () => {
fetch.json.mockImplementationOnce(() =>
Promise.resolve({
latest: "1.0.0",
})
);

const opts = new Map(baseOptions);
const tags = await npmDistTag.add("@scope/some-pkg@1.0.1", undefined, opts);

expect(tags).toEqual({
latest: "1.0.1",
});
});
});

describe("npmDistTag.remove()", () => {
it("removes an existing dist-tag for a given package", async () => {
fetch.json.mockImplementationOnce(() =>
Promise.resolve({
latest: "1.0.0",
"removed-tag": "1.0.1",
})
);

const opts = new Map(baseOptions);
const tags = await npmDistTag.remove("@scope/some-pkg@1.0.1", "removed-tag", opts);

expect(tags).not.toHaveProperty("removed-tag");
expect(fetch).toHaveBeenLastCalledWith(
"-/package/@scope%2fsome-pkg/dist-tags/removed-tag",
expect.objectContaining({
method: "DELETE",
})
);
});

it("supports custom registry", () => {
ChildProcessUtilities.execSync.mockReturnValue("target-tag");
it("does not attempt removal of nonexistent tag", async () => {
const opts = new Map(baseOptions);
const tags = await npmDistTag.remove("@scope/some-pkg@1.0.1", "missing-tag", opts);

expect(npmDistTag.check(pkg, "target-tag", { registry })).toBe(true);
expect(tags).toEqual({});
expect(fetch).not.toHaveBeenCalled();
expect(stubLog.info).toHaveBeenLastCalledWith(
"dist-tag",
'"missing-tag" is not a dist-tag on @scope/some-pkg'
);
});
});

expect(ChildProcessUtilities.execSync).toHaveBeenLastCalledWith("npm", ["dist-tag", "ls", pkg.name], {
cwd: pkg.location,
env: {
npm_config_registry: registry,
},
pkg,
});
describe("npmDistTag.list()", () => {
it("returns dictionary of dist-tags", async () => {
fetch.json.mockImplementationOnce(() =>
Promise.resolve({
latest: "1.0.0",
"other-tag": "1.0.1",
})
);

const opts = new Map(baseOptions);
const tags = await npmDistTag.list("@scope/some-pkg", opts);

expect(tags).toEqual({
latest: "1.0.0",
"other-tag": "1.0.1",
});
expect(fetch.json).toHaveBeenLastCalledWith(
"-/package/@scope%2fsome-pkg/dist-tags",
expect.objectContaining({
spec: expect.objectContaining({
name: "@scope/some-pkg",
}),
})
);
});
});

0 comments on commit 54008c6

Please sign in to comment.