Skip to content

Commit

Permalink
Fix package.json updates during publish (#744)
Browse files Browse the repository at this point in the history
Object.prototype.toJSON() is normally called when passed to JSON.stringify(),
but write-pkg iterates Object.keys() before serializing so it has to be
explicitly called here (otherwise it mangles the instance properties).

This also adds universal interpolation of __TEST_VERSION__ inside fixture JSON files with the current version being tested. This is necessary for the new lerna-publish integration test, and is nice to have for all unit tests anyway.

Fixes #741
  • Loading branch information
evocateur authored Apr 7, 2017
1 parent 634d16d commit ad7e214
Show file tree
Hide file tree
Showing 16 changed files with 257 additions and 74 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@
"eslint-plugin-flowtype": "^2.30.4",
"jest": "^19.0.2",
"normalize-newline": "^3.0.0",
"normalize-path": "^2.1.1"
"normalize-path": "^2.1.1",
"replacestream": "^4.0.2"
},
"jest": {
"collectCoverageFrom": [
Expand Down
5 changes: 4 additions & 1 deletion src/commands/PublishCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,10 @@ export default class PublishCommand extends Command {
this.updatePackageDepsObject(pkg, "peerDependencies", exact);

// write new package
writePkg.sync(packageJsonLocation, pkg);
writePkg.sync(packageJsonLocation, pkg.toJSON());
// NOTE: Object.prototype.toJSON() is normally called when passed to
// JSON.stringify(), but write-pkg iterates Object.keys() before serializing
// so it has to be explicit here (otherwise it mangles the instance properties)

// we can now generate the Changelog, based on the
// the updated version that we're about to release.
Expand Down
9 changes: 6 additions & 3 deletions test/Package.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,12 @@ describe("Package", () => {

describe(".toJSON()", () => {
it("should return internal package for serialization", () => {
expect(JSON.stringify(pkg, null, 2)).toBe(
JSON.stringify(pkg._package, null, 2)
);
expect(pkg.toJSON()).toBe(pkg._package);

const implicit = JSON.stringify(pkg, null, 2);
const explicit = JSON.stringify(pkg._package, null, 2);

expect(implicit).toBe(explicit);
});
});

Expand Down
3 changes: 3 additions & 0 deletions test/config/integration.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
],
"testEnvironment": "node",
"setupTestFrameworkScriptFile": "<rootDir>/test/integration/_setupTestFramework.js",
"snapshotSerializers": [
"<rootDir>/test/helpers/replaceLernaVersion.js"
],
"verbose": true
}
2 changes: 1 addition & 1 deletion test/fixtures/BootstrapCommand/integration/lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"lerna": "2.0.0-beta.38",
"lerna": "__TEST_VERSION__",
"packages": [
"package-*"
],
Expand Down
15 changes: 15 additions & 0 deletions test/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// this file is not transpiled by Jest when required in replaceLernaVersion.js
"use strict";

const path = require("path");
const pkg = require("../../package.json");

/**
Shared constants for tests
**/
exports.REPO_ROOT = path.resolve(__dirname, "../..");
exports.LERNA_BIN = path.resolve(exports.REPO_ROOT, pkg.bin.lerna);
exports.LERNA_VERSION = pkg.version;

// placeholder used in fixture JSON files, replaced during tests
exports.__TEST_VERSION__ = "__TEST_VERSION__";
34 changes: 34 additions & 0 deletions test/helpers/fixtureUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import path from "path";
import fs from "fs-promise";
import { padStart } from "lodash";
import execa from "execa";
import replaceStream from "replacestream";

import { LERNA_VERSION, __TEST_VERSION__ } from "./constants";

export function getTempDir(fixtureName) {
// e.g., "lerna-1490053388515-663678-BootstrapCommand_01_basic"
Expand Down Expand Up @@ -49,3 +52,34 @@ export function fixtureNamer() {
return parts.join("_");
};
}

/**
During fixture copy, replace "__TEST_VERSION__" with the current version.
This is primarily for integration tests, but doesn't hurt unit tests.
@param {stream.Readable} readStream
@param {stream.Writable} writeStream
@param {Object} file metadata
@property {String} file.name source path of file being copied
@see https://github.com/jprichardson/node-fs-extra/blob/master/lib/copy/ncp.js#L105
**/
function transform(readStream, writeStream, file) {
let stream = readStream;

if (path.extname(file.name) === ".json") {
stream = stream.pipe(replaceStream(__TEST_VERSION__, LERNA_VERSION));
}

writeStream.on("open", () => {
stream.pipe(writeStream);
});
}

export function copyFixture(fixturePath, testDir) {
const fixtureDir = path.resolve(__dirname, `../fixtures/${fixturePath}`);

return fs.copy(fixtureDir, testDir, {
transform,
});
}
6 changes: 2 additions & 4 deletions test/helpers/initExternalFixture.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from "path";
import fs from "fs-promise";
import {
copyFixture,
fixtureNamer,
getTempDir,
gitInit,
Expand All @@ -13,13 +12,12 @@ const createdDirectories = [];
afterAll(() => removeAll(createdDirectories));

export default function initExternalFixture(fixturePath) {
const fixtureDir = path.resolve(__dirname, `../fixtures/${fixturePath}`);
const fixtureName = getFixtureName(fixturePath);

return getTempDir(fixtureName).then((testDir) => {
createdDirectories.push(testDir);

return fs.copy(fixtureDir, testDir)
return copyFixture(fixturePath, testDir)
.then(() => gitInit(testDir, "Init external commit"))
.then(() => testDir);
});
Expand Down
6 changes: 2 additions & 4 deletions test/helpers/initFixture.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from "path";
import fs from "fs-promise";
import {
copyFixture,
fixtureNamer,
getTempDir,
gitInit,
Expand All @@ -13,13 +12,12 @@ const createdDirectories = [];
afterAll(() => removeAll(createdDirectories));

export default function initFixture(fixturePath) {
const fixtureDir = path.resolve(__dirname, `../fixtures/${fixturePath}`);
const fixtureName = getFixtureName(fixturePath);

return getTempDir(fixtureName).then((testDir) => {
createdDirectories.push(testDir);

return fs.copy(fixtureDir, testDir)
return copyFixture(fixturePath, testDir)
.then(() => gitInit(testDir))
.then(() => testDir);
});
Expand Down
18 changes: 10 additions & 8 deletions test/helpers/replaceLernaVersion.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import _ from "lodash";
import normalizeNewline from "normalize-newline";
// this file is not transpiled by Jest when configured in "snapshotSerializers"
"use strict";

const PLACEHOLDER = "__TEST_VERSION__";
const VERSION = require("../../package.json").version;
const REGEX = new RegExp(`v?${VERSION}`, "gm");
const _ = require("lodash");
const normalizeNewline = require("normalize-newline");
const constants = require("./constants");

const REGEX = new RegExp(`v?${constants.LERNA_VERSION}`, "gm");
// TODO: maybe less naïve regex?

function needsReplacement(str) {
return str.indexOf(PLACEHOLDER) === -1;
return str.indexOf(constants.__TEST_VERSION__) === -1;
}

function stableVersion(str) {
return str.replace(REGEX, PLACEHOLDER);
return str.replace(REGEX, constants.__TEST_VERSION__);
}

const stabilizeString = _.flow([
Expand All @@ -25,7 +27,7 @@ with __TEST_VERSION__ when found in snapshotted strings or object properties.
@see http://facebook.github.io/jest/docs/expect.html#expectaddsnapshotserializerserializer
**/
export default {
module.exports = {
test(thing) {
return _.isString(thing) && needsReplacement(thing) || (
_.isPlainObject(thing) && _.isString(thing.lerna) && needsReplacement(thing.lerna)
Expand Down
29 changes: 0 additions & 29 deletions test/integration/__snapshots__/lerna-bootstrap.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,3 @@ Successfully ran npm script 'test' in packages:
- @integration/package-3
- package-4"
`;

exports[`stdout: simple 1`] = `
"Lerna __TEST_VERSION__
Bootstrapping 4 packages
Preinstalling packages
Installing external dependencies
Symlinking packages and binaries
Postinstalling packages
Prepublishing packages
Successfully bootstrapped 4 packages."
`;

exports[`stdout: simple 2`] = `
"Lerna __TEST_VERSION__
package-1
package-2
cli package-2 OK
package-3 cli1 OK
package-3 cli2 package-2 OK
Successfully ran npm script 'test' in packages:
- @integration/package-1
- @integration/package-2
- @integration/package-3
- package-4"
`;
100 changes: 100 additions & 0 deletions test/integration/__snapshots__/lerna-publish.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`packages: updates fixed versions 1`] = `
Array [
Object {
"name": "package-1",
"version": "1.0.1",
},
Object {
"dependencies": Object {
"package-1": "^1.0.1",
},
"name": "package-2",
"version": "1.0.1",
},
Object {
"devDependencies": Object {
"package-2": "^1.0.1",
},
"name": "package-3",
"version": "1.0.1",
},
Object {
"dependencies": Object {
"package-1": "^0.0.0",
},
"name": "package-4",
"version": "1.0.1",
},
Object {
"dependencies": Object {
"package-1": "^1.0.1",
},
"name": "package-5",
"private": true,
"version": "1.0.1",
},
]
`;

exports[`packages: updates independent versions 1`] = `
Array [
Object {
"name": "package-1",
"version": "2.0.0",
},
Object {
"dependencies": Object {
"package-1": "^2.0.0",
},
"name": "package-2",
"version": "3.0.0",
},
Object {
"devDependencies": Object {
"package-2": "^3.0.0",
},
"name": "package-3",
"version": "4.0.0",
},
Object {
"dependencies": Object {
"package-1": "^0.0.0",
},
"name": "package-4",
"version": "5.0.0",
},
]
`;

exports[`stdout: updates fixed versions 1`] = `
"Lerna __TEST_VERSION__
Current version: 1.0.0
Checking for updated packages...
No tags found! Comparing with initial commit.
Changes:
- package-1: 1.0.0 => 1.0.1
- package-2: 1.0.0 => 1.0.1
- package-3: 1.0.0 => 1.0.1
- package-4: 1.0.0 => 1.0.1
- package-5: 1.0.0 => 1.0.1 (private)
Assuming confirmation."
`;

exports[`stdout: updates independent versions 1`] = `
"Lerna __TEST_VERSION__
Independent Versioning Mode
Checking for updated packages...
No tags found! Comparing with initial commit.
Changes:
- package-1: 1.0.0 => 2.0.0
- package-2: 2.0.0 => 3.0.0
- package-3: 3.0.0 => 4.0.0
- package-4: 4.0.0 => 5.0.0
Assuming confirmation."
`;
17 changes: 5 additions & 12 deletions test/integration/lerna-bootstrap.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import fs from "fs-promise";
import path from "path";
import execa from "execa";
import readPkg from "read-pkg";
import writePkg from "write-pkg";
import initFixture from "../helpers/initFixture";
import replaceLernaVersion from "../helpers/replaceLernaVersion";
import { LERNA_BIN, REPO_ROOT, LERNA_VERSION } from "../helpers/constants";

expect.addSnapshotSerializer(replaceLernaVersion);

const ROOTDIR = path.resolve(__dirname, "../..");
const PACKAGE = readPkg.sync(ROOTDIR);
const VERSION = PACKAGE.version;
const TGZ_SRC = path.join(ROOTDIR, `lerna-${VERSION}.tgz`);
const LERNA = path.join(ROOTDIR, PACKAGE.bin.lerna);
const TGZ_SRC = path.join(REPO_ROOT, `lerna-${LERNA_VERSION}.tgz`);

const copyTarball = (cwd) =>
fs.copy(TGZ_SRC, path.join(cwd, "lerna-latest.tgz"));
Expand All @@ -26,14 +19,14 @@ const npmTestInDir = (cwd) =>

describe("lerna bootstrap", () => {
describe("from CLI", () => {
test.concurrent("bootstraps all packages", () => {
test.skip("bootstraps all packages", () => {
return initFixture("BootstrapCommand/integration").then((cwd) => {
return Promise.resolve()
.then(() => execa(LERNA, ["bootstrap"], { cwd }))
.then(() => execa(LERNA_BIN, ["bootstrap"], { cwd }))
.then((result) => {
expect(result.stdout).toMatchSnapshot("stdout: simple");
})
.then(() => execa(LERNA, ["run", "test", "--", "--silent"], { cwd }))
.then(() => execa(LERNA_BIN, ["run", "test", "--", "--silent"], { cwd }))
.then((result) => {
expect(result.stdout).toMatchSnapshot("stdout: simple");
});
Expand Down
Loading

0 comments on commit ad7e214

Please sign in to comment.