Files
meteor/tools/tests/package-tests.js
Hugh Willson 41b4383349 Adjust self-test match / timeout settings to help reduce Circle test failures (#8933)
* Fix CircleCI failures by adjusting the timing of problematic tests.

* Wait longer for Mongo to start.

* Increase lint wait time; run logs show we're close to the current timeout.
2017-07-21 09:24:20 -04:00

958 lines
31 KiB
JavaScript

var _= require('underscore');
var selftest = require('../tool-testing/selftest.js');
var Sandbox = selftest.Sandbox;
var files = require('../fs/files.js');
var testUtils = require('../tool-testing/test-utils.js');
var utils = require('../utils/utils.js');
var packageClient = require('../packaging/package-client.js');
var catalog = require('../packaging/catalog/catalog.js');
var username = "test";
// Returns a random package name.
var randomizedPackageName = function (username, start) {
// We often use package names in long, wrapped string output, so having them
// be a consistent length is very useful.
var startStr = start ? start + "-" : "";
return username + ":" + startStr + utils.randomToken().substring(0, 6);
}
// Given a sandbox, that has the app as its currend cwd, read the packages file
// and check that it contains exactly the packages specified, in order.
//
// sand: a sandbox, that has the main app directory as its cwd.
// packages: an array of packages in order. Packages can be of the form:
//
// meteor-base (ie: name), in which case this will match any
// version of that package as long as it is included.
//
// awesome-pack@1.0.0 (ie: name@version) to match that name at that
// version explicitly. This is for packages that we included at a specific
// version.
var checkPackages = selftest.markStack(function(sand, packages) {
var lines = sand.read(".meteor/packages").split("\n");
var i = 0;
_.each(lines, function(line) {
if (!line) {
return;
}
// If the specified package contains an @ sign, then it has a version
// number, so we should match everything.
if (packages[i].split('@').length > 1) {
selftest.expectEqual(line, packages[i]);
} else {
var pack = line.split('@')[0];
selftest.expectEqual(pack, packages[i]);
}
i++;
});
selftest.expectEqual(packages.length, i);
});
// Given a sandbox, that has the app as its currend cwd, read the versions file
// and check that it contains the packages that we are looking for. We don't
// check the order, we just want to make sure that the right dependencies are
// in.
//
// sand: a sandbox, that has the main app directory as its cwd.
// packages: an array of packages in order. Packages can be of the form:
//
// meteor-base (ie: name), in which case this will match any
// version of that package as long as it is included. This is for packages
// external to the app, since we don't want this test to fail when we push a
// new version.
//
// awesome-pack@1.0.0 (ie: name@version) to match that name at that
// version explicitly. This is for packages that only exist for the purpose
// of this test (for example, packages local to this app), so we know exactly
// what version we expect.
var checkVersions = selftest.markStack(function(sand, packages) {
var lines = sand.read(".meteor/versions").split("\n");
var depend = {};
_.each(lines, function(line) {
if (!line) {
return;
}
// Packages are stored of the form foo@1.0.0, so this should give us an
// array [foo, 1.0.0].
var split = line.split('@');
var pack = split[0];
depend[pack] = split[1];
});
var i = 0;
_.each(packages, function (pack) {
var split = pack.split('@');
if (split.length > 1) {
selftest.expectEqual(depend[split[0]], split[1]);
} else {
var exists = _.has(depend, split[0]);
selftest.expectEqual(exists, true);
}
i++;
});
selftest.expectEqual(packages.length, i);
});
// Add packages to an app. Change the contents of the packages and their
// dependencies, make sure that the app still refreshes.
selftest.define("change packages during hot code push", [], function () {
var s = new Sandbox();
var run;
s.set("METEOR_WATCH_PRIORITIZE_CHANGED", "false");
// Starting a run
s.createApp("myapp", "package-tests");
s.cd("myapp");
run = s.run();
run.waitSecs(5);
run.match("myapp");
run.match("proxy");
run.waitSecs(5);
run.match("MongoDB");
run.waitSecs(5);
run.match("your app");
run.waitSecs(5);
run.match("running at");
run.match("localhost");
// Add the local package 'say-something'. It should print a message.
s.write(".meteor/packages", "meteor-base \n say-something");
run.waitSecs(3);
run.match("initial");
// Modify the local package 'say-something'.
s.cd("packages/say-something", function () {
s.write("foo.js", "console.log(\"another\");");
});
run.waitSecs(12);
run.match("another");
// Add a local package depends-on-plugin.
s.write(".meteor/packages", "meteor-base \n depends-on-plugin");
run.waitSecs(2);
run.match("foobar");
// Change something in the plugin.
s.cd("packages/contains-plugin/plugin", function () {
s.write("plugin.js", "console.log(\"edit\");");
});
run.waitSecs(2);
run.match("edit");
run.match("foobar!");
// Check that we are watching the versions file, as well as the packages file.
s.unlink('.meteor/versions');
run.waitSecs(10);
run.match("restarted");
// Switch back to say-something for a moment.
s.write(".meteor/packages", "meteor-base \n say-something");
run.waitSecs(3);
run.match("another");
run.stop();
s.rename('packages/say-something', 'packages/shout-something');
s.write(".meteor/packages", "meteor-base \n shout-something");
s.cd("packages/shout-something", function () {
s.write("foo.js", "console.log(\"louder\");");
});
run = s.run();
run.waitSecs(5);
run.match("myapp");
run.match("proxy");
run.match("MongoDB");
run.waitSecs(5);
run.match("louder"); // the package actually loaded
// How about breaking and fixing a package.js?
s.cd("packages/shout-something", function () {
var packageJs = s.read("package.js");
s.write("package.js", "]");
run.waitSecs(3);
run.match("=> Errors prevented startup");
run.match("package.js:1: Unexpected token");
run.match("Waiting for file change");
s.write("package.js", packageJs);
run.waitSecs(3);
run.match("restarting");
run.match("restarted");
});
run.stop();
});
// Add packages through the command line. Make sure that the correct set of
// changes is reflected in .meteor/packages, .meteor/versions and list.
selftest.define("add packages to app", [], function () {
var s = new Sandbox();
var run;
// Starting a run
s.createApp("myapp", "package-tests");
s.cd("myapp");
s.set("METEOR_OFFLINE_CATALOG", "t");
// This has legit version syntax, but accounts-base started with 1.0.0 and is
// unlikely to backtrack.
run = s.run("add", "accounts-base@0.123.123");
run.matchErr("no such version");
run.expectExit(1);
// Adding a nonexistent package at a nonexistent version should print
// only one error message, not two. (We used to print "no such
// package" and "no such version".)
run = s.run("add", "not-a-real-package-and-never-will-be@1.0.0");
run.matchErr("no such package");
run.expectExit(1);
run.forbidAll("no such version");
run = s.run("add", "accounts-base");
run.match("accounts-base: A user account system");
run.expectExit(0);
checkPackages(s,
["meteor-base", "accounts-base"]);
// Adding the nonexistent version now should still say "no such
// version". Regression test for
// https://github.com/meteor/meteor/issues/2898.
run = s.run("add", "accounts-base@0.123.123");
run.matchErr("no such version");
run.expectExit(1);
run.forbidAll("Currently using accounts-base");
run.forbidAll("will be changed to");
run = s.run("--once");
run = s.run("add", "say-something@1.0.0");
run.match("say-something: print to console");
run.expectExit(0);
checkPackages(s,
["meteor-base", "accounts-base", "say-something@1.0.0"]);
run = s.run("add", "depends-on-plugin");
run.match(/depends-on-plugin.*added,/);
run.expectExit(0);
checkPackages(s,
["meteor-base", "accounts-base",
"say-something@1.0.0", "depends-on-plugin"]);
checkVersions(s,
["accounts-base", "depends-on-plugin",
"say-something", "meteor-base",
"contains-plugin@1.1.0"]);
run = s.run("remove", "say-something");
run.match("say-something: removed dependency");
checkVersions(s,
["accounts-base", "depends-on-plugin",
"meteor-base",
"contains-plugin"]);
run = s.run("remove", "depends-on-plugin");
run.match(/contains-plugin.*removed from your project/);
run.match(/depends-on-plugin.*removed from your project/);
run.match("depends-on-plugin: removed dependency");
checkVersions(s,
["accounts-base",
"meteor-base"]);
run = s.run("list");
run.match("accounts-base");
run.match("meteor-base");
// Add a description-less package. Check that no weird things get
// printed (like "added no-description: undefined").
run = s.run("add", "no-description");
run.match("no-description\n");
run.expectEnd();
run.expectExit(0);
});
selftest.define("add debugOnly and prodOnly packages", [], function () {
var s = new Sandbox();
var run;
// Starting a run
s.createApp("myapp", "package-tests");
s.cd("myapp");
s.set("METEOR_OFFLINE_CATALOG", "t");
// Add a debugOnly package. It should work during a normal run, but print
// nothing in production mode.
run = s.run("add", "debug-only");
run.match("debug-only");
run.expectExit(0);
s.mkdir("server");
s.write("server/exit-test.js",
"process.exit(global.DEBUG_ONLY_LOADED ? 234 : 235)");
run = s.run("--once");
run.waitSecs(15);
run.expectExit(234);
run = s.run("--once", "--production");
run.waitSecs(15);
run.expectExit(235);
// Add prod-only package, which sets GLOBAL.PROD_ONLY_LOADED.
run = s.run("add", "prod-only");
run.match("prod-only");
run.expectExit(0);
s.mkdir("server");
s.write("server/exit-test.js", // overwrite
"process.exit(global.PROD_ONLY_LOADED ? 234 : 235)");
run = s.run("--once");
run.waitSecs(15);
run.expectExit(235);
run = s.run("--once", "--production");
run.waitSecs(15);
run.expectExit(234);
});
selftest.define("add package with both debugOnly and prodOnly", [], function () {
var s = new Sandbox();
var run;
// Add an app with a package with prodOnly and debugOnly set (an error)
s.createApp("myapp", "debug-only-test", {dontPrepareApp: true});
s.cd("myapp");
run = s.run("--prepare-app");
run.waitSecs(20);
run.matchErr("can't have more than one of: debugOnly, prodOnly, testOnly");
run.expectExit(1);
});
// Add a package that adds files to specific client architectures.
selftest.define("add packages client archs", function (options) {
var runTestWithArgs = function (clientType, args, port) {
var s = new Sandbox({
clients: _.extend(options.clients, { port: port })
});
// Starting a run
s.createApp("myapp", "package-tests");
s.cd("myapp");
s.set("METEOR_OFFLINE_CATALOG", "t");
var outerRun = s.run("add", "say-something-client-targets");
outerRun.match(/say-something-client-targets.*added,/);
outerRun.expectExit(0);
checkPackages(s, ["meteor-base", "say-something-client-targets"]);
var expectedLogNum = 0;
s.testWithAllClients(function (run) {
run.waitSecs(5);
run.match("myapp");
run.match("proxy");
run.waitSecs(5);
run.match("MongoDB");
run.waitSecs(5);
run.match("running at");
run.match("localhost");
run.connectClient();
run.waitSecs(20);
run.match("all clients " + (expectedLogNum++));
run.match(clientType + " client " + (expectedLogNum++));
run.stop();
}, args);
};
runTestWithArgs("browser", [], 3000);
});
// `packageName` should be a full package name (i.e. <username>:<package
// name>), and the sandbox should be logged in as that username.
var createAndPublishPackage = selftest.markStack(function (s, packageName) {
var packageDirName = "package-of-two-versions";
s.createPackage(packageDirName, packageName, "package-of-two-versions");
s.cd(packageDirName, function (){
var run = s.run("publish", "--create");
run.waitSecs(25);
run.expectExit(0);
});
return packageDirName;
});
selftest.define("add package with no builds", ["net"], function () {
var s = new Sandbox();
// This depends on glasser:binary-package-with-no-builds@1.0.0 existing with
// no published builds.
s.createApp("myapp", "empty");
s.cd("myapp");
var run = s.run("add", "glasser:binary-package-with-no-builds");
run.waitSecs(10);
run.matchErr("glasser:binary-package-with-no-builds@1.0.0");
run.matchErr("No compatible binary build found");
run.expectExit(1);
});
selftest.define("package skeleton creates correct versionsFrom", ['custom-warehouse'], function () {
var s = new Sandbox({ warehouse: { v1: { recommended: true } } });
var token = utils.randomToken();
var fullPackageName = "test:" + token;
var fsPackageName = token;
var run = s.run("create", "--package", fullPackageName);
run.waitSecs(15);
run.match(fullPackageName);
run.expectExit(0);
s.cd(fsPackageName);
var packageJs = s.read("package.js");
if (! packageJs.match(/api.versionsFrom\('v1'\);/)) {
selftest.fail("package.js missing correct 'api.versionsFrom':\n" +
packageJs);
}
});
selftest.define("show unknown version of package", function () {
var s = new Sandbox();
// This version doesn't exist and is unlikely to exist.
var run = s.run("show", "meteor-base@0.123.456");
run.waitSecs(5);
run.matchErr("meteor-base@0.123.456: not found");
run.expectExit(1);
});
selftest.define("circular dependency errors", function () {
var s = new Sandbox();
// meteor add refreshes, but we don't need anything from the official catalog
// here.
s.set('METEOR_OFFLINE_CATALOG', 't');
var run;
// This app contains some pairs of packages with circular dependencies The app
// currently *uses* no packages, so it can be created successfully.
s.createApp("myapp", "circular-deps");
s.cd("myapp");
// Try to add one of a pair of circularly-depending packages. See an error.
run = s.run('add', 'first');
run.matchErr('error: circular dependency');
run.expectExit(1);
// Note that the app still builds fine because 'first' didn't actually get
// added.
run = s.run('--prepare-app');
run.expectExit(0);
// This pair has first-imply uses second-imply, second-imply implies
// first-imply.
run = s.run('add', 'first-imply');
run.matchErr('error: circular dependency');
run.expectExit(1);
// This pair has first-weak uses second-weak, second-weak uses first-weak
// weakly. Currently, it's possible to add a weak cycle to an app (ie, the
// prepare-app step passes), but not to run the bundler. We don't want to
// write a test that prevents us from making the weak cycle an error at
// prepare-time, so let's skip straight to bundling.
s.write('.meteor/packages', 'first-weak');
run = s.run('--once');
run.matchErr('error: circular dependency');
run.expectExit(254);
// ... but we can add second-weak, which just doesn't pull in first-weak at
// all.
s.write('.meteor/packages', 'second-weak');
run = s.run('--once');
run.match(/first-weak.*removed from your project/);
run.expectExit(123); // the app immediately calls process.exit(123)
// This pair has first-unordered uses second-unordered, second-unordered uses
// first-unordered unorderedly. This should work just fine: that's why
// unordered exists!
s.write('.meteor/packages', 'first-unordered');
run = s.run('--once');
run.match(/first-unordered.*added/);
run.match(/second-unordered.*added/);
run.match(/second-weak.*removed from your project/);
run.expectExit(123); // the app immediately calls process.exit(123)
});
// Runs 'meteor show <fullPackageName>' without a specified version and checks
// that the output is correct.
//
// - s: sandbox in which to run commands
// - fullPackageName: name of the package to show.
// - options:
// - summary: Expected summary of the latest version.
// - description: longform description of the latest version
// - maintainers: the string of maintainers
// - homepage: Homepage url, if one was set.
// - git: Git url, if one was set.
// - exports: exports string
// - implies: implies string
// - defaultVersion: version that git/exports/etc come from
// - versions: array of objects representing versions that we have
// published, with keys:
// - version: version number (ex: 0.9.9)
// - date: string we expect to see as the date.
// - label: string that we expect to see as the label. (ex: "installed")
// - addendum: a message to display at the bottom.
// - all: run 'meteor show' with the 'show-all' option.
var testShowPackage = selftest.markStack(function (s, fullPackageName, options) {
var run;
if (options.all) {
run = s.run("show", "--show-all", fullPackageName);
} else {
run = s.run("show", fullPackageName);
}
var packageName = options.defaultVersion ?
fullPackageName + "@" + options.defaultVersion : fullPackageName;
run.match("Package: " + packageName + "\n");
if (options.homepage) {
run.read("Homepage: " + options.homepage + "\n");
}
if (options.maintainers) {
run.read("Maintainers: " + options.maintainers + "\n");
}
if (options.git) {
run.read("Git: " + options.git + "\n");
}
if (options.exports) {
run.read("Exports: " + options.exports + "\n");
}
if (options.implies) {
run.read("Implies: " + options.implies + "\n");
}
run.read("\n");
if (_.has(options, "description")) {
run.read(options.description + "\n");
} else if (_.has(options, "summary")) {
run.read(options.summary + "\n");
}
if (options.versions) {
if (options.all) {
run.match("Versions:");
} else {
run.match("Recent versions:");
}
_.each(options.versions, function (version) {
run.match(version.version);
if (version.directory) {
run.match(version.directory + "\n");
} else {
run.match(version.date);
if (version.label) {
run.match(version.label + "\n");
} else {
run.match("\n");
}
}
});
run.read("\n");
}
if (options.addendum) {
run.read(options.addendum);
}
run.expectExit(0);
});
// Runs 'meteor show <name>@<version> and checks that the output is correct.
//
// - s: sandbox
// - options:
// - packageName: name of the package.
// - version: version string.
// - summary: summary string of the package.
// - description: long-form description of the package
// - publishedBy: username of the publisher.
// - publishedOn: string of the publication time.
// - git: (optional) URL of the git repository.
// - dependencies: (optional) an array of objects representing dependencies:
// - name: package name
// - constraint: constraint, such as "1.0.0" or "=1.0.0" or null.
// - weak: true if this is a weak dependency.
// - addendum: a message that we expect to display at the very bottom.
var testShowPackageVersion = selftest.markStack(function (s, options) {
var name = options.packageName;
var version = options.version;
var run = s.run("show", name + "@" + version);
run.match("Package: " + name + "@" + version + "\n");
if (options.directory) {
run.match("Directory: " + options.directory + "\n");
}
if (options.exports) {
run.read("Exports: " + options.exports + "\n");
}
if (options.implies) {
run.read("Implies: " + options.implies + "\n");
}
if (options.git) {
run.match("Git: " + options.git + "\n");
}
if (_.has(options, "description")) {
run.read("\n");
run.read(options.description + "\n");
} else if (_.has(options, "summary")) {
run.read("\n");
run.read(options.summary + "\n");
}
if (options.dependencies) {
run.read("\n");
run.read("Depends on:\n");
// Use 'read' to ensure that these are the only dependencies listed.
_.each(options.dependencies, function (dep) {
var depStr = dep.name;
if (dep.constraint) {
depStr += "@" + dep.constraint;
}
if (dep.weak) {
depStr += " (weak dependency)";
}
run.read(" " + depStr + "\n");
});
}
if (options.publishedBy) {
run.match("\n");
run.match(
"Published by " + options.publishedBy +
" on " + options.publishedOn + ".\n");
}
if (options.addendum) {
run.read("\n" + options.addendum + "\n");
}
// Make sure that we exit without printing anything else.
run.expectEnd(0);
});
// For local packages without a version, we want to replace version information
// with the string "local". We also want to make sure that querying for
// 'name@local' gives that local version.
selftest.define("show local package w/o version", function () {
var s = new Sandbox();
var name = "my-local-package" + utils.randomToken();
// Create a package without version or summary; check that we can show its
// information without crashing.
s.createPackage(name, name, "package-for-show");
var packageDir = files.pathJoin(s.root, "home", name);
s.cd(name, function () {
s.cp("package-completely-empty.js", "package.js");
testShowPackage(s, name, {
defaultVersion: "local",
versions: [{ version: "local", directory: packageDir }]
});
testShowPackageVersion(s, {
packageName: name,
version: "local",
directory: packageDir
});
// Test that running without any arguments also shows this package.
var run = s.run("show");
run.match("Package: " + name + "@local\n");
run.match("Directory: " + packageDir + "\n");
run.expectExit(0);
});
// Test that running without any arguments outside of a package does not
// work.
var run = s.run("show");
run.matchErr("specify a package or release name");
run.expectExit(1);
});
// Make sure that a local-only package shows up correctly in show and search
// results.
selftest.define("show and search local package", function () {
// Setup: create an app, containing a package. This local package should show
// up in the results of `meteor show` and `meteor search`.
var s = new Sandbox();
var name = "my-local-package" + utils.randomToken();
s.createApp("myapp", "empty");
s.cd("myapp");
s.mkdir("packages");
s.cd("packages", function () {
s.createPackage(name, name, "package-for-show");
});
var packageDir = files.pathJoin(s.root, "home", "myapp", "packages", name);
s.cd(packageDir, function () {
s.cp("package-with-git.js", "package.js");
});
var summary = 'This is a test package';
// Run `meteor show`, but don't add the package to the app yet. We should know
// that the package exists, even though it hasn't been added to the app.
testShowPackage(s, name, {
summary: summary,
defaultVersion: "local",
git: 'www.github.com/meteor/meteor',
versions: [{ version: "1.0.0", directory: packageDir }]
});
// Add the package to the app.
var run = s.run("add", name);
run.waitSecs(5);
run.expectExit(0);
testShowPackage(s, name, {
summary: summary,
git: 'www.github.com/meteor/meteor',
defaultVersion: "local",
versions: [{ version: "1.0.0", directory: packageDir }]
});
// When we run `meteor search`, we should be able to see the results for this
// package, even though it does not exist on the server.
run = s.run("search", name);
run.waitSecs(15);
run.match(name);
run.match("You can use");
run.expectExit(0);
// We can see exports on local packages.
s.cd("packages");
summary = "This is a test package";
name = "my-local-exports";
packageDir = files.pathJoin(s.root, "home", "myapp", "packages", name);
s.createPackage(name, name, "package-for-show");
s.cd(name, function () {
s.cp("package-with-exports.js", "package.js");
});
var exportStr =
"A, B (server), C (web.browser, web.cordova), D (web.browser), E (web.cordova), G (server, web.cordova)";
var description = "Test package.";
testShowPackage(s, name, {
summary: summary,
git: "www.github.com/meteor/meteor",
exports: exportStr,
description: description,
defaultVersion: "local",
versions: [{ version: "1.0.1", directory: packageDir }]
});
testShowPackageVersion(s, {
packageName: name,
version: "1.0.1",
directory: packageDir,
git: "www.github.com/meteor/meteor",
summary: summary,
exports: exportStr,
description: description
});
// Test showing implies. Since we are not going to build the package, we don't
// have to publish any of the things that we imply.
var impRaw = {
A: "",
B: "server",
C: "web.browser, web.cordova",
D: "web.browser",
E: "web.cordova",
G: "server, web.cordova"
};
var impliesData = _.sortBy(_.map(impRaw, function (label, placeholder) {
var name = randomizedPackageName(username, placeholder.toLowerCase());
return { placeholder: placeholder, name: name, label: label};
}), 'name');
s.cd(name, function () {
s.cp("package-with-implies.js", "package.js");
var packOpen = s.read("package.js");
_.each(impliesData, function (d) {
var repReg = new RegExp("~" + d.placeholder + "~", "g");
packOpen = packOpen.replace(repReg, d.name);
});
s.write("package.js", packOpen);
});
summary = "This is a test package";
description = "Test package.";
var impArr = _.map(impliesData, function (d) {
return d.label ? d.name + " (" + d.label + ")" : d.name;
});
var impStr =
impArr[0] + ", " + impArr[1] + ", " +
impArr[2] + ", " + impArr[3] + ", " +
impArr[4] + ", " + impArr[5];
testShowPackage(s, name, {
summary: summary,
description: description,
implies: impStr,
directory: packageDir,
defaultVersion: "local",
git: "www.github.com/meteor/meteor",
versions: [{ version: "1.2.1", directory: packageDir }]
});
// Implies are also dependencies.
var deps = _.map(impliesData, function (d) {
return { name: d.name, constraint: "1.0.0" };
});
testShowPackageVersion(s, {
packageName: name,
version: "1.2.1",
directory: packageDir,
description: description,
summary: summary,
git: "www.github.com/meteor/meteor",
implies: impStr,
dependencies: deps
});
});
// This tests that we get the right excerpt out of the Readme.md in different
// combinations. It doesn't test publication, because publishing is slow --
// that's covered in a different test.
selftest.define("show readme excerpt", function () {
var s = new Sandbox();
var name = "my-local-package" + utils.randomToken();
// Create a package without version or summary; check that we can show its
// information without crashing.
s.createPackage(name, name, "package-for-show");
var packageDir = files.pathJoin(s.root, "home", name);
// We are just going to change the description in the Readme. Some things
// about this package are not going to change, and our test will be more
// legible to factor them out here.
var basePackageInfo = {
summary: "This is a test package",
defaultVersion: "local",
versions: [{ version: "0.9.9", directory: packageDir }]
};
var baseVersionInfo = {
summary: "This is a test package",
packageName: name,
version: "0.9.9",
directory: packageDir
};
s.cd(name);
// By default, we will use the README.md file for documentation.
// Start with a blank file. Nothing should show up!
s.write("README.md", "");
testShowPackage(s, name, basePackageInfo);
testShowPackageVersion(s, baseVersionInfo);
// An example of a standard readme.
var readme =
"Heading" + "\n" +
"========" + "\n" +
"foobar1" + "\n" +
"\n" +
"## Subheading" + "\n" +
"You should not see this line!";
s.write("README.md", readme);
testShowPackage(
s, name, _.extend({ description: "foobar1" }, basePackageInfo));
testShowPackageVersion(
s, _.extend({ description: "foobar1" }, baseVersionInfo));
// Another example -- we have hidden the excerpt under a different subheading.
readme =
"Heading" + "\n" +
"========" + "\n" +
"## Subheading" + "\n" +
"foobar2" + "\n" +
"## Another subheading" + "\n" +
"You should not see this line!";
s.write("README.md", readme);
testShowPackage(
s, name, _.extend({ description: "foobar2" }, basePackageInfo));
testShowPackageVersion(
s, _.extend({ description: "foobar2" }, baseVersionInfo));
// Generally, people skip a line between the header and the text, and
// sometimes, even between headers. (It is part of markdown, in fact.) Let's
// make sure that we handle that correctly.
readme =
"Heading" + "\n" +
"========" + "\n" + "\n" +
"## Subheading" + "\n" + "\n" +
"foobaz" + "\n" + "\n" +
"## Another subheading" + "\n" + "\n" +
"You should not see this line!";
s.write("README.md", readme);
testShowPackage(
s, name, _.extend({ description: "foobaz" }, basePackageInfo));
testShowPackageVersion(
s, _.extend({ description: "foobaz" }, baseVersionInfo));
// Some formatting in the text.
var excerpt =
"Here is a code sample:" + "\n\n" +
"```foobar and foobar```";
readme =
"Heading" + "\n" +
"========" + "\n" + "\n" +
excerpt + "\n\n" +
"# Subheading" + "\n" + "\n" +
"## Another subheading" + "\n" + "\n" +
"You should not see this line!";
s.write("README.md", readme);
testShowPackage(
s, name, _.extend({ description: excerpt }, basePackageInfo));
testShowPackageVersion(
s, _.extend({ description: excerpt }, baseVersionInfo));
// Now, let's try different file specifications for the documentation.
var git = "https:ilovegit.git";
var summary = "Test summary";
var staging = s.read("package-customizable.js");
staging = staging.replace(/~version~/g, "1.0.0");
staging = staging.replace(/~git~/g, git);
staging = staging.replace(/~summary~/g, summary);
// If we specify null, we have no docs.
s.write("package.js", staging.replace(/~documentation~/g, "null"));
baseVersionInfo = {
summary: summary,
git: git,
packageName: name,
version: "1.0.0",
directory: packageDir
};
basePackageInfo = {
git: git,
summary: summary,
defaultVersion: "local",
versions: [{ version: "1.0.0", directory: packageDir }]
};
testShowPackageVersion(s, baseVersionInfo);
testShowPackage(s, name, basePackageInfo);
// If we specify a different file, read that file.
s.write("package.js",
staging.replace(/~documentation~/g, "'Meteor-Readme.md'"));
readme = "A special Readme, just for Meteor.";
s.write("Meteor-Readme.md", "Title\n==\n" + readme);
testShowPackageVersion(s,
_.extend({ description: readme }, baseVersionInfo));
testShowPackage(s, name,
_.extend({ description: readme }, basePackageInfo));
// If we specify a non-existent file, tell us.
s.write("package.js",
staging.replace(/~documentation~/g, "'NOTHING'"));
var run = s.run("show", name);
run.matchErr("Documentation not found");
run.expectExit(1);
run = s.run("show", name + "@1.0.0");
run.matchErr("Documentation not found");
run.expectExit(1);
});