diff --git a/commands/list/README.md b/commands/list/README.md index 2e3042ac66..8ff25a792a 100644 --- a/commands/list/README.md +++ b/commands/list/README.md @@ -4,13 +4,23 @@ ## Usage +The `list` subcommand is aliased to several convenient shorthands (similar to [`npm ls`](https://docs.npmjs.com/cli/ls)): + + * `lerna ls`: Identical to `lerna list`, which is itself analogous to the `ls` command + * `lerna ll`: Equivalent to `lerna ls -l`, showing [long](#--long) output + * `lerna la`: Equivalent to `lerna ls -la`, showing [all](#--all) packages (including private ones) + ```sh -# The following commands are identical: -$ lerna list $ lerna ls +package-1 +package-2 ``` -List all of the public packages in the current Lerna repo. +You might notice extra logging from `lerna` when running these commands in your shell. +Rest assured they will not infect your piped incantations, +as all logs are emitted to `stderr`, not `stdout`. + +In any case, you can always pass `--loglevel silent` to create pristine chains of magical shell wizardry. ## Options @@ -18,18 +28,84 @@ List all of the public packages in the current Lerna repo. ### --json +Show information as a JSON array. + ```sh $ lerna ls --json -``` - -When run with this flag, `ls` will return an array of objects in the following format: - -```json [ { - "name": "package", + "name": "package-1", + "version": "1.0.0", + "private": false, + "location": "/path/to/packages/pkg-1/" + }, + { + "name": "package-2", "version": "1.0.0", - "private": false + "private": false, + "location": "/path/to/packages/pkg-2/" } ] ``` + +**Tip:** Pipe to the [`json`](http://trentm.com/json/) utility to pick out individual properties: + +```sh +$ lerna ls --json --all | json -a -c 'this.private === true' name +package-3 +``` + +### --all + +Alias: `-a` + +Show private packages that are hidden by default. + +```sh +$ lerna ls --all +package-1 +package-2 +package-3 (private) +``` + +### --long + +Alias: `-l` + +Show extended information. + +```sh +$ lerna ls --long +package-1 v1.0.1 packages/pkg-1 +package-2 v1.0.2 packages/pkg-2 + +$ lerna ls -la +package-1 v1.0.1 packages/pkg-1 +package-2 v1.0.2 packages/pkg-2 +package-3 v1.0.3 packages/pkg-3 (private) +``` + +### --parseable + +Alias: `-p` + +Show parseable output instead of columnified view. + +By default, each line of the output is an absolute path to a package. + +In `--long` output, each line is a `:`-separated list: `::[:flags..]` + +```sh +$ lerna ls --parseable +/path/to/packages/pkg-1 +/path/to/packages/pkg-2 + +$ lerna ls -pl +/path/to/packages/pkg-1:package-1:1.0.1 +/path/to/packages/pkg-2:package-2:1.0.2 + +$ lerna ls -pla +/path/to/packages/pkg-1:package-1:1.0.1 +/path/to/packages/pkg-2:package-2:1.0.2 +/path/to/packages/pkg-3:package-3:1.0.3:PRIVATE +``` diff --git a/commands/list/__tests__/__snapshots__/list-command.test.js.snap b/commands/list/__tests__/__snapshots__/list-command.test.js.snap deleted file mode 100644 index 77eb25d07d..0000000000 --- a/commands/list/__tests__/__snapshots__/list-command.test.js.snap +++ /dev/null @@ -1,125 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LsCommand filtering excludes packages when --ignore is a brace-expanded list 1`] = ` -"package-a-1 v1.0.0 -package-a-2 v1.0.0" -`; - -exports[`LsCommand filtering excludes packages when --ignore is a glob 1`] = ` -"package-a-1 v1.0.0 -package-a-2 v1.0.0" -`; - -exports[`LsCommand filtering excludes packages when --ignore is a package name 1`] = ` -"package-4 v1.0.0 -package-a-1 v1.0.0 -package-a-2 v1.0.0" -`; - -exports[`LsCommand filtering filters packages when both --scope and --ignore are passed 1`] = `"package-a-1 v1.0.0"`; - -exports[`LsCommand filtering includes all packages when --scope is omitted 1`] = ` -"package-3 v1.0.0 -package-4 v1.0.0 -package-a-1 v1.0.0 -package-a-2 v1.0.0" -`; - -exports[`LsCommand filtering includes packages when --scope is a glob 1`] = ` -"package-a-1 v1.0.0 -package-a-2 v1.0.0" -`; - -exports[`LsCommand filtering includes packages when --scope is a package name 1`] = `"package-3 v1.0.0"`; - -exports[`LsCommand in a Yarn workspace should use package.json/workspaces setting 1`] = ` -"package-1 v1.0.0 -package-2 v1.0.0" -`; - -exports[`LsCommand in a basic repo does not list changes for ignored packages 1`] = `"package-1 v1.0.0"`; - -exports[`LsCommand in a basic repo lists changes for a given scope 1`] = `"package-1 v1.0.0"`; - -exports[`LsCommand in a basic repo should list packages 1`] = ` -"package-1 v1.0.0 -package-2 v1.0.0 -package-3 v1.0.0 -package-4 v1.0.0 -package-5 v1.0.0 (private)" -`; - -exports[`LsCommand in a repo with packages outside of packages/ should list packages 1`] = ` -"package-3 v1.0.0 -package-1 v1.0.0 -package-2 v1.0.0" -`; - -exports[`LsCommand with --include-filtered-dependencies should list packages, including filtered ones 1`] = ` -"@test/package-2 v1.0.0 -@test/package-1 v1.0.0" -`; - -exports[`LsCommand with --include-filtered-dependents should list packages, including filtered ones 1`] = ` -"@test/package-1 v1.0.0 -@test/package-2 v1.0.0" -`; - -exports[`LsCommand with --json should list packages as json objects 1`] = ` -Array [ - Object { - "name": "package-1", - "private": false, - "version": "1.0.0", - }, - Object { - "name": "package-2", - "private": false, - "version": "1.0.0", - }, - Object { - "name": "package-3", - "private": false, - "version": "1.0.0", - }, - Object { - "name": "package-4", - "private": false, - "version": "1.0.0", - }, - Object { - "name": "package-5", - "private": true, - "version": "1.0.0", - }, -] -`; - -exports[`LsCommand with an undefined version should list packages 1`] = `"package-1 MISSING"`; - -exports[`LsCommand with fancy 'packages' configuration lists globstar-nested packages 1`] = ` -"globstar v1.0.0 -package-2 v1.0.0 -package-4 v1.0.0 -package-1 v1.0.0 -package-3 v1.0.0 -package-5 v1.0.0" -`; - -exports[`LsCommand with fancy 'packages' configuration lists packages under explicitly configured node_modules directories 1`] = ` -"alle-pattern-root v1.0.0 -package-1 v1.0.0 -package-2 v1.0.0 -package-3 v1.0.0 -package-4 v1.0.0 -@scoped/package-5 v1.0.0" -`; - -exports[`LsCommand with terribly complicated dependency cycles should list all packages with no repeats 1`] = ` -"package-1 v1.0.0 -package-2 v1.0.0 -package-3 v1.0.0 -package-4 v1.0.0 -package-5 v1.0.0 -package-6 v1.0.0" -`; diff --git a/commands/list/__tests__/list-command.test.js b/commands/list/__tests__/list-command.test.js index fe8baddb1e..f9d2c09005 100644 --- a/commands/list/__tests__/list-command.test.js +++ b/commands/list/__tests__/list-command.test.js @@ -1,5 +1,10 @@ "use strict"; +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const normalizePath = require("normalize-path"); + // mocked modules const output = require("@lerna/output"); @@ -9,28 +14,122 @@ const initFixture = require("@lerna-test/init-fixture")(__dirname); // file under test const lernaLs = require("@lerna-test/command-runner")(require("../command")); -describe("LsCommand", () => { +// remove quotes around strings +expect.addSnapshotSerializer({ test: val => typeof val === "string", print: val => val }); + +// TODO: extract root dir serializer? +const TEMP_DIR = fs.realpathSync(os.tmpdir()); +const ROOT_DIR = new RegExp(path.join(TEMP_DIR, "[0-9a-f]+"), "g"); + +// normalize temp directory paths in snapshots +expect.addSnapshotSerializer({ + test: val => typeof val === "string" && ROOT_DIR.test(val), + print: val => normalizePath(val.replace(ROOT_DIR, "")), +}); + +describe("lerna ls", () => { describe("in a basic repo", () => { - it("should list packages", async () => { - const testDir = await initFixture("basic"); + let testDir; + + beforeAll(async () => { + testDir = await initFixture("basic"); + }); + + it("should list public packages", async () => { await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-1 +package-2 +package-3 +package-4 +`); }); - it("lists changes for a given scope", async () => { - const testDir = await initFixture("basic"); + it("should also list private packages with --all", async () => { + await lernaLs(testDir)("--all"); + expect(output.logged()).toMatchInlineSnapshot(` +package-1 +package-2 +package-3 +package-4 +package-5 (PRIVATE) +`); + }); + + it("lists public package versions and relative paths with --long", async () => { + await lernaLs(testDir)("--long"); + expect(output.logged()).toMatchInlineSnapshot(` +package-1 v1.0.0 packages/package-1 +package-2 v1.0.0 packages/package-2 +package-3 v1.0.0 packages/package-3 +package-4 v1.0.0 packages/package-4 +`); + }); + + it("lists all package versions and relative paths with --long --all", async () => { + await lernaLs(testDir)("-la"); + expect(output.logged()).toMatchInlineSnapshot(` +package-1 v1.0.0 packages/package-1 +package-2 v1.0.0 packages/package-2 +package-3 v1.0.0 packages/package-3 +package-4 v1.0.0 packages/package-4 +package-5 v1.0.0 packages/package-5 (PRIVATE) +`); + }); + + it("lists public package locations with --parseable", async () => { + await lernaLs(testDir)("--parseable"); + expect(output.logged()).toMatchInlineSnapshot(` +/packages/package-1 +/packages/package-2 +/packages/package-3 +/packages/package-4 +`); + }); + + it("lists all package locations with --parseable --all", async () => { + await lernaLs(testDir)("-pa"); + expect(output.logged()).toMatchInlineSnapshot(` +/packages/package-1 +/packages/package-2 +/packages/package-3 +/packages/package-4 +/packages/package-5 +`); + }); + + it("lists public package locations with --parseable --long", async () => { + await lernaLs(testDir)("--parseable", "--long"); + expect(output.logged()).toMatchInlineSnapshot(` +/packages/package-1:package-1:1.0.0 +/packages/package-2:package-2:1.0.0 +/packages/package-3:package-3:1.0.0 +/packages/package-4:package-4:1.0.0 +`); + }); + + it("lists all package locations with --parseable --long --all", async () => { + await lernaLs(testDir)("-pal"); + expect(output.logged()).toMatchInlineSnapshot(` +/packages/package-1:package-1:1.0.0 +/packages/package-2:package-2:1.0.0 +/packages/package-3:package-3:1.0.0 +/packages/package-4:package-4:1.0.0 +/packages/package-5:package-5:1.0.0:PRIVATE +`); + }); + + it("lists packages matching --scope", async () => { await lernaLs(testDir)("--scope", "package-1"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(`package-1`); }); - it("does not list changes for ignored packages", async () => { - const testDir = await initFixture("basic"); + it("does not list packages matching --ignore", async () => { await lernaLs(testDir)("--ignore", "package-@(2|3|4|5)"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(`package-1`); }); it("does not list private packages with --no-private", async () => { - const testDir = await initFixture("basic"); await lernaLs(testDir)("--no-private"); expect(output.logged()).not.toMatch("package-5 v1.0.0 (private)"); }); @@ -40,42 +139,89 @@ describe("LsCommand", () => { it("should list packages", async () => { const testDir = await initFixture("extra"); await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-3 +package-1 +package-2 +`); }); }); - describe("with --include-filtered-dependencies", () => { + describe("--include-filtered-dependencies", () => { it("should list packages, including filtered ones", async () => { const testDir = await initFixture("include-filtered-dependencies"); await lernaLs(testDir)("--scope", "@test/package-2", "--include-filtered-dependencies"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +@test/package-2 +@test/package-1 +`); }); }); - describe("with --include-filtered-dependents", () => { + describe("--include-filtered-dependents", () => { it("should list packages, including filtered ones", async () => { const testDir = await initFixture("include-filtered-dependencies"); await lernaLs(testDir)("--scope", "@test/package-1", "--include-filtered-dependents"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +@test/package-1 +@test/package-2 +`); }); }); describe("with an undefined version", () => { - it("should list packages", async () => { + it("replaces version with MISSING", async () => { const testDir = await initFixture("undefined-version"); - await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + await lernaLs(testDir)("--long"); + expect(output.logged()).toMatchInlineSnapshot(`package-1 MISSING packages/package-1`); + }); + + it("appends MISSING flag to long parseable output", async () => { + const testDir = await initFixture("undefined-version"); + await lernaLs(testDir)("--long", "--parseable"); + expect(output.logged()).toMatchInlineSnapshot(`/packages/package-1:package-1:MISSING`); }); }); - describe("with --json", () => { + describe("--json", () => { it("should list packages as json objects", async () => { const testDir = await initFixture("basic"); - await lernaLs(testDir)("--json"); + await lernaLs(testDir)("--json", "-a"); // Output should be a parseable string const jsonOutput = JSON.parse(output.logged()); - expect(jsonOutput).toMatchSnapshot(); + expect(jsonOutput).toEqual([ + { + location: expect.stringContaining("package-1"), + name: "package-1", + private: false, + version: "1.0.0", + }, + { + location: expect.stringContaining("package-2"), + name: "package-2", + private: false, + version: "1.0.0", + }, + { + location: expect.stringContaining("package-3"), + name: "package-3", + private: false, + version: "1.0.0", + }, + { + location: expect.stringContaining("package-4"), + name: "package-4", + private: false, + version: "1.0.0", + }, + { + location: expect.stringContaining("package-5"), + name: "package-5", + private: true, + version: "1.0.0", + }, + ]); }); }); @@ -83,7 +229,10 @@ describe("LsCommand", () => { it("should use package.json/workspaces setting", async () => { const testDir = await initFixture("yarn-workspaces"); await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-1 +package-2 +`); }); }); @@ -98,7 +247,14 @@ describe("LsCommand", () => { await lernaLs(testDir)("--scope", "package-1", "--include-filtered-dependencies"); // should follow all transitive deps and pass all packages except 7 with no repeats - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-1 +package-2 +package-3 +package-4 +package-5 +package-6 +`); }); }); @@ -106,13 +262,27 @@ describe("LsCommand", () => { it("lists globstar-nested packages", async () => { const testDir = await initFixture("globstar"); await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +globstar +package-2 +package-4 +package-1 +package-3 +package-5 +`); }); it("lists packages under explicitly configured node_modules directories", async () => { const testDir = await initFixture("explicit-node-modules"); await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +alle-pattern-root +package-1 +package-2 +package-3 +package-4 +@scoped/package-5 +`); }); it("throws an error when globstars and explicit node_modules configs are mixed", async () => { @@ -137,37 +307,55 @@ describe("LsCommand", () => { it("includes all packages when --scope is omitted", async () => { await lernaLs(testDir)(); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-3 +package-4 +package-a-1 +package-a-2 +`); }); it("includes packages when --scope is a package name", async () => { await lernaLs(testDir)("--scope", "package-3"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(`package-3`); }); it("excludes packages when --ignore is a package name", async () => { await lernaLs(testDir)("--ignore", "package-3"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-4 +package-a-1 +package-a-2 +`); }); it("includes packages when --scope is a glob", async () => { await lernaLs(testDir)("--scope", "package-a-*"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-a-1 +package-a-2 +`); }); it("excludes packages when --ignore is a glob", async () => { await lernaLs(testDir)("--ignore", "package-@(2|3|4)"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-a-1 +package-a-2 +`); }); it("excludes packages when --ignore is a brace-expanded list", async () => { await lernaLs(testDir)("--ignore", "package-{3,4}"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(` +package-a-1 +package-a-2 +`); }); it("filters packages when both --scope and --ignore are passed", async () => { await lernaLs(testDir)("--scope", "package-a-*", "--ignore", "package-a-2"); - expect(output.logged()).toMatchSnapshot(); + expect(output.logged()).toMatchInlineSnapshot(`package-a-1`); }); it("throws an error when --scope is lacking an argument", async () => { diff --git a/commands/list/command.js b/commands/list/command.js index cfe4aaff29..0ccfc757d2 100644 --- a/commands/list/command.js +++ b/commands/list/command.js @@ -7,7 +7,7 @@ const filterable = require("@lerna/filter-options"); */ exports.command = "list"; -exports.aliases = ["ls"]; +exports.aliases = ["ls", "la", "ll"]; exports.describe = "List local packages"; @@ -15,9 +15,27 @@ exports.builder = yargs => { yargs.options({ json: { group: "Command Options:", - describe: "Show information in JSON format", + describe: "Show information as a JSON array", type: "boolean", }, + a: { + group: "Command Options:", + describe: "Show private packages that are normally hidden", + type: "boolean", + alias: "all", + }, + l: { + group: "Command Options:", + describe: "Show extended information", + type: "boolean", + alias: "long", + }, + p: { + group: "Command Options:", + describe: "Show parseable output instead of columnified view", + type: "boolean", + alias: "parseable", + }, }); return filterable(yargs); diff --git a/commands/list/index.js b/commands/list/index.js index 98b7afb61f..766508fbc9 100644 --- a/commands/list/index.js +++ b/commands/list/index.js @@ -2,7 +2,7 @@ const chalk = require("chalk"); const columnify = require("columnify"); - +const path = require("path"); const Command = require("@lerna/command"); const output = require("@lerna/output"); @@ -18,11 +18,14 @@ class ListCommand extends Command { } initialize() { - this.resultList = this.filteredPackages.map(pkg => ({ - name: pkg.name, - version: pkg.version, - private: pkg.private, - })); + const alias = this.options._[0]; + + this.showAll = alias === "la" || this.options.all; + this.showLong = alias === "la" || alias === "ll" || this.options.long; + + this.resultList = this.showAll + ? this.filteredPackages + : this.filteredPackages.filter(pkg => !pkg.private); // logged after output this.count = this.resultList.length; @@ -33,6 +36,8 @@ class ListCommand extends Command { if (this.options.json) { result = this.formatJSON(); + } else if (this.options.parseable) { + result = this.formatParseable(); } else { result = this.formatColumns(); } @@ -43,7 +48,38 @@ class ListCommand extends Command { } formatJSON() { - return JSON.stringify(this.resultList, null, 2); + // explicit re-mapping exposes non-enumerable properties + const data = this.resultList.map(pkg => ({ + name: pkg.name, + version: pkg.version, + private: pkg.private, + location: pkg.location, + })); + + return JSON.stringify(data, null, 2); + } + + formatParseable() { + const mapper = this.showLong + ? pkg => { + const result = [pkg.location, pkg.name]; + + // sometimes the version is inexplicably missing? + if (pkg.version) { + result.push(pkg.version); + } else { + result.push("MISSING"); + } + + if (pkg.private) { + result.push("PRIVATE"); + } + + return result.join(":"); + } + : pkg => pkg.location; + + return this.resultList.map(mapper).join("\n"); } formatColumns() { @@ -53,20 +89,23 @@ class ListCommand extends Command { }; if (result.version) { - formatted.version = chalk.grey(`v${result.version}`); + formatted.version = chalk.green(`v${result.version}`); } else { formatted.version = chalk.yellow("MISSING"); } if (result.private) { - formatted.private = `(${chalk.red("private")})`; + formatted.private = `(${chalk.red("PRIVATE")})`; } + formatted.location = chalk.grey(path.relative(".", result.location)); + return formatted; }); return columnify(formattedResults, { showHeaders: false, + columns: this.getColumnOrder(), config: { version: { align: "right", @@ -74,6 +113,20 @@ class ListCommand extends Command { }, }); } + + getColumnOrder() { + const columns = ["name"]; + + if (this.showLong) { + columns.push("version", "location"); + } + + if (this.showAll) { + columns.push("private"); + } + + return columns; + } } module.exports.ListCommand = ListCommand; diff --git a/integration/__fixtures__/lerna-ls/lerna.json b/integration/__fixtures__/lerna-ls/lerna.json new file mode 100644 index 0000000000..053f72e27e --- /dev/null +++ b/integration/__fixtures__/lerna-ls/lerna.json @@ -0,0 +1,3 @@ +{ + "version": "independent" +} diff --git a/integration/__fixtures__/lerna-ls/package.json b/integration/__fixtures__/lerna-ls/package.json new file mode 100644 index 0000000000..ee2e1ab116 --- /dev/null +++ b/integration/__fixtures__/lerna-ls/package.json @@ -0,0 +1,6 @@ +{ + "name": "list-integration", + "description": "fodder for lerna ls integration tests", + "version": "0.0.0-monorepo", + "private": true +} diff --git a/integration/__fixtures__/lerna-ls/packages/pkg-1/package.json b/integration/__fixtures__/lerna-ls/packages/pkg-1/package.json new file mode 100644 index 0000000000..4f6fb24a38 --- /dev/null +++ b/integration/__fixtures__/lerna-ls/packages/pkg-1/package.json @@ -0,0 +1,5 @@ +{ + "name": "package-1", + "description": "a normal package", + "version": "1.0.0" +} diff --git a/integration/__fixtures__/lerna-ls/packages/pkg-2/package.json b/integration/__fixtures__/lerna-ls/packages/pkg-2/package.json new file mode 100644 index 0000000000..86cbc7f14f --- /dev/null +++ b/integration/__fixtures__/lerna-ls/packages/pkg-2/package.json @@ -0,0 +1,5 @@ +{ + "name": "@test/package-2", + "description": "a scoped package", + "version": "2.0.0" +} diff --git a/integration/__fixtures__/lerna-ls/packages/pkg-3/package.json b/integration/__fixtures__/lerna-ls/packages/pkg-3/package.json new file mode 100644 index 0000000000..de3ece76e1 --- /dev/null +++ b/integration/__fixtures__/lerna-ls/packages/pkg-3/package.json @@ -0,0 +1,4 @@ +{ + "name": "package-3", + "description": "a package missing a version, somehow" +} diff --git a/integration/__fixtures__/lerna-ls/packages/pkg-4/package.json b/integration/__fixtures__/lerna-ls/packages/pkg-4/package.json new file mode 100644 index 0000000000..293ea7e032 --- /dev/null +++ b/integration/__fixtures__/lerna-ls/packages/pkg-4/package.json @@ -0,0 +1,6 @@ +{ + "name": "package-4", + "description": "a private package", + "version": "4.0.0", + "private": true +} diff --git a/integration/lerna-ls.test.js b/integration/lerna-ls.test.js new file mode 100644 index 0000000000..de1a644210 --- /dev/null +++ b/integration/lerna-ls.test.js @@ -0,0 +1,101 @@ +"use strict"; + +const cliRunner = require("@lerna-test/cli-runner"); +const initFixture = require("@lerna-test/init-fixture")(__dirname); +const normalizeNewline = require("normalize-newline"); + +// remove quotes around strings +expect.addSnapshotSerializer({ test: val => typeof val === "string", print: val => val }); + +// ls never makes changes to repo, so we only need one fixture + runner +let lerna; + +beforeAll(async () => { + const cwd = await initFixture("lerna-ls"); + const run = cliRunner(cwd); + + // wrap runner to remove trailing whitespace added by columnify + // and normalize temp directory roots + lerna = (...args) => + run(...args).then(result => + normalizeNewline(result.stdout) + .split("\n") + .map(line => line.trimRight().replace(cwd, "")) + .join("\n") + ); +}); + +test("lerna list", async () => { + const stdout = await lerna("list"); + expect(stdout).toMatchInlineSnapshot(` +package-1 +@test/package-2 +package-3 +`); +}); + +test("lerna ls", async () => { + const stdout = await lerna("ls"); + expect(stdout).toMatchInlineSnapshot(` +package-1 +@test/package-2 +package-3 +`); +}); + +test("lerna ls --all", async () => { + const stdout = await lerna("ls", "--all"); + expect(stdout).toMatchInlineSnapshot(` +package-1 +@test/package-2 +package-3 +package-4 (PRIVATE) +`); +}); + +test("lerna ls --long", async () => { + const stdout = await lerna("ls", "--long"); + expect(stdout).toMatchInlineSnapshot(` +package-1 v1.0.0 packages/pkg-1 +@test/package-2 v2.0.0 packages/pkg-2 +package-3 MISSING packages/pkg-3 +`); +}); + +test("lerna ls --parseable", async () => { + const stdout = await lerna("ls", "--parseable"); + expect(stdout).toMatchInlineSnapshot(` +/packages/pkg-1 +/packages/pkg-2 +/packages/pkg-3 +`); +}); + +test("lerna ls --all --long --parseable", async () => { + const stdout = await lerna("ls", "-alp"); + expect(stdout).toMatchInlineSnapshot(` +/packages/pkg-1:package-1:1.0.0 +/packages/pkg-2:@test/package-2:2.0.0 +/packages/pkg-3:package-3:MISSING +/packages/pkg-4:package-4:4.0.0:PRIVATE +`); +}); + +test("lerna la", async () => { + const stdout = await lerna("la"); + expect(stdout).toMatchInlineSnapshot(` +package-1 v1.0.0 packages/pkg-1 +@test/package-2 v2.0.0 packages/pkg-2 +package-3 MISSING packages/pkg-3 +package-4 v4.0.0 packages/pkg-4 (PRIVATE) +`); +}); + +test("lerna ll", async () => { + const stdout = await lerna("ll"); + expect(stdout).toMatchInlineSnapshot(` +package-1 v1.0.0 packages/pkg-1 +@test/package-2 v2.0.0 packages/pkg-2 +package-3 MISSING packages/pkg-3 +`); +});