Files
meteor/tools/tests/releases.js
ekatek 24a4ed9bdc Automatically line-wrap output
Includes the following changes to Console.js:

- Console.info, Console.warn, Console.debug and Console.error now automatically
  line-wrap the output to 80 characters, or the width of the terminal screen (if
  known). This is in line with our current style guide on how things should be wrapped!

- Sometimes, there are parts of text that we don't want to line-wrap. For example, if we are
  telling the user to run 'meteor long-command --with --options' we don't want to
  have a newline in the middle of that! Wrap those commands in Console.command, like
  this:
     Console.info("something and then run", Console.command(command), "and then");
  This also makes them bold if chalk is on, as a nice bonus. So, if we ever turn
  chalk back on, the bolding of commands will be more consistent.

- Sometimes, there is bulkier output that we don't want to format at all, including
  line-wrapping: log snippets, stack traces, JSON output, etc. In that case, we can use
  Console.rawInfo, Console.rawError, Console.rawWarn and Console.rawDebug. Don't use
  Console.command inside the raw* functions! It won't be processed (at all).

- There are fancier things that we can do, other than just simply wrapping things.
  We can indent:
  "  Start here and then when wrapping
     continue over here".

  We frequently do this for commands, for example. In the past, we did this manually --
  but we can't do this for long messages that might get wrapped, and anyway, it is
  good to codify this instead of counting spaces. Allows us to be better about consistency,
  for example.

- We can also add a bulletPoint, which is a small notice in the beginning that looks like
  this:
  " => Start here and then when wrapping
       continue below the bulletPoint".

  Since it is a elss intuitive option, I have wrapped most of the time that we use a
  bulletPoint into helper functions on the Console.js.

- Some common bulletpoints that we use are:
    ASCII Checkboxes (Console.success)
    ASCII X-s (Console.failWarn and Console.failInfo)
    =>  (Console.arrowError, Console.arrowWarn, Console.arrowInfo)
    WARNING (Console.labelWarn)

  The => are sometimes indented, so they take an optional indent argument, showing how
  many spaces to indent by.

The wrapper interface would be less complicated, if there was a more unified conceit behind our
terminal messages. If there is one, it is not documented. My hope is that, in many cases,
moving these to Console will make it easier for someone with great product sense to
clean up our terminal messages. It will also make it easier to write such messages, since
it will be easier to follow an accepted standard.

In the codebase outside of Console:

- Went through and looked at our use of Console.error/info/etc, replacing with rawError/etc
  whenever approporiate.

- Went through and modified most of 'stdout' and 'stderr' calls to use the new functions.
  I made an exception for stuff that doesn't want a new line at the end, or otherwise does
  weird things (ex: print user logs directly), on the basis that, at this juncture, it is
  better to be safe than to be sorry.

- Long messages no longer need to break the code style guide by ignoring indentation rules.
  Fixed that where approporiate.

- Fixed the tests! A number of our stock messages are actually longer than 80 chars.

- Personal favourite: The Android license agreement is now line wrapped! Much better experience.

- There is some more work to do on:
  - longform help (currently comes with built-in linebreaks, would have to change the entire
    mechanism for how that works)
  - Buildmessage sometimes has headers that start with =>, but they are short. I didn't want to
    pass wrapper options all the way to main.captureOrExit before merging the rest of this and
    making sure that we like it. Since these messages are fairly short, I don't think that's
    likely to be a serious problem.

I hope that this makes life easier for us in the future! No more counting chars, no more breaking
the style guide. Better experience for users with wider terminals (or even shorter terminals!).
Let's give this a try.
2014-12-04 17:56:04 -08:00

249 lines
7.6 KiB
JavaScript

var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;
var files = require('../files.js');
var catalog = require('../catalog.js');
// XXX: Why is this an internet using test? Because our warehouse is a
// hackhackhack. If we clean up the hackhackhackhack, then this does not need
// the internets. (Or, to be more specific: our warehouse code tries to fetch
// the packages from the internet. If we could fool it into using local packages
// instead, or think that it alreayd has the packages, it would be ok). (This is
// because it calls 'create' from a warehouse, to be specific).
selftest.define("springboard", ['checkout', 'net'], function () {
var s = new Sandbox({
warehouse: {
v1: { },
v2: { recommended: true }
}
});
var run;
// If run not in an app dir, runs the latest version ...
run = s.run("--version");
run.read('Meteor v2\n');
run.expectEnd();
run.expectExit(0);
// ... unless you asked for a different one.
run = s.run("--version", "--release", "METEOR@v1");
run.read('Meteor v1\n');
run.expectEnd();
run.expectExit(0);
// Apps are created with the latest release ...
run = s.run("create", "myapp");
run.waitSecs(5);
run.expectExit(0);
s.cd('myapp', function () {
run = s.run("--version");
run.read('Meteor v2\n');
run.expectExit(0);
});
// ... unless you asked for a different one.
run = s.run("create", "myapp2", "--release", "METEOR@v1").expectExit(0);
s.cd('myapp2', function () {
run = s.run("--version");
run.read('Meteor v1\n');
run.expectExit(0);
});
// Suppose you ask for a release that doesn't exist.
s.set('METEOR_TEST_FAIL_RELEASE_DOWNLOAD', 'not-found');
run = s.run("--release", "weird");
run.readErr("Meteor weird: unknown release.\n");
run.expectEnd();
run.expectExit(1);
// Suppose you're offline and you ask for a release you don't have
// cached.
// XXX On the refreshpolicy branch, we removed some of the support
// code for this test. Make sure we get it to pass before merging.
s.set('METEOR_TEST_FAIL_RELEASE_DOWNLOAD', 'offline');
run = s.run("--release", "weird");
run.matchErr("offline");
run.matchErr("weird: unknown release");
run.expectExit(1);
// Project asking for nonexistent release.
s.cd('myapp2', function () {
s.write(".meteor/release", "strange");
s.set('METEOR_TEST_FAIL_RELEASE_DOWNLOAD', 'not-found');
run = s.run();
run.matchErr("uses Meteor strange");
run.matchErr("don't have it either");
run.expectExit(1);
// You're offline and project asks for non-cached release.
s.set('METEOR_TEST_FAIL_RELEASE_DOWNLOAD', 'offline');
run = s.run();
run.matchErr("offline");
run.matchErr("it uses Meteor strange");
run.matchErr("don't have that version");
run.matchErr("of Meteor installed");
run.matchErr("update servers");
run.expectExit(1);
// You create an app from a checkout, and then try to use it from an
// install without setting a release on it.
s.unset('METEOR_TEST_FAIL_RELEASE_DOWNLOAD');
s.write(".meteor/release", "none");
run = s.run("--requires-release");
run.matchErr("must specify");
run.matchErr("permanently set");
run.expectExit(1);
// As previous, but you pass --release to manually pick a release.
run = s.run("--version", "--release", "v1");
run.expectExit(0);
run.forbidAll("must specify");
run.forbidAll("permanently set");
// You use modern Meteor with a super old release from the dark ages
// before the .meteor/release file. You get an error.
s.unlink('.meteor/release');
run = s.run("--version");
run.matchErr("does not have a .meteor/release file");
run.matchErr("edit the .meteor/release file");
run.expectExit(1);
// .meteor/release exists but is empty. You get an error.
s.write(".meteor/release", "\n");
run = s.run("--version");
run.matchErr("release file which is empty");
run.expectExit(1);
// XXX Test springboard to pre-0.9.0 release
});
});
// XXX: Why is this an internet using test? Because our warehouse is a
// hackhackhack. If we clean up the hackhackhackhack, then this does not need
// the internets. (Or, to be more specific: our warehouse code tries to fetch
// the packages from the internet. If we could fool it into using local packages
// instead, or think that it already has the packages, it would be ok).
selftest.define("writing versions file", ['checkout', 'net'], function () {
var s = new Sandbox({
warehouse: {
v1: { recommended: true},
v2: { recommended: true }
}
});
var run;
// Create an app with the latest release.
run = s.run("create", "myapp");
run.waitSecs(15);
run.expectExit(0);
s.cd('myapp');
run = s.run("--version");
run.read('Meteor v2\n');
run.expectExit(0);
// Check the contents of the versions file.
var versions = s.read('.meteor/versions');
if (!versions) {
selftest.fail("Versions file NOT written in new app.");
}
// Remove the versions file.
s.unlink('.meteor/versions');
// Run with --release, do not change versions file.
run = s.run("list", "--release", "v1");
run.expectExit(0);
versions = s.read('.meteor/versions');
if (versions) {
selftest.fail("Versions file written with --release.");
}
// Update with --release.
run = s.run("update", "--release", "v1");
run.expectExit(0);
// version file should exist.
versions = s.read('.meteor/versions');
if (!versions) {
selftest.fail("Versions file NOT written after update");
}
});
selftest.define("checkout", ['checkout'], function () {
var s = new Sandbox;
var run;
// Can't specify a release when running Meteor from a checkout
run = s.run("--release", "v1");
run.waitSecs(5);
run.matchErr("Can't specify");
run.expectExit(1);
// You get a warning banner when the checkout overrides the release
// that an app is pinned to
s.createApp('myapp', 'standard-app');
s.cd('myapp', function () {
s.write(".meteor/release", "something");
run = s.run("list");
run.readErr("=> Running Meteor from a checkout");
run.matchErr("project version");
run.matchErr("(Meteor something)\n");
run.expectExit(0);
});
});
selftest.define("download release", ['net', 'slow'], function () {
var s, run;
if (files.inCheckout())
s = new Sandbox({ warehouse: { v1: { tools: 'tools1', latest: true } } });
else
s = new Sandbox;
// End-to-end, online test of downloading and springboarding. This
// release was built from the
// 'release/release-used-to-test-springboarding' tag in GitHub. All
// it does is print this string and exit.
run = s.run("--release", "release-used-to-test-springboarding");
run.waitSecs(1000);
run.match("THIS IS A FAKE RELEASE ONLY USED TO TEST ENGINE SPRINGBOARDING");
run.expectExit();
});
selftest.define("unknown release", [], function () {
var s = new Sandbox({
warehouse: {
v2: { recommended: true }
}
});
s.set("METEOR_OFFLINE_CATALOG", "t");
var run;
s.createApp('myapp', 'packageless', { dontPrepareApp: true });
s.cd('myapp');
run = s.run("--release", "bad");
run.matchErr("Meteor bad: unknown release");
// METEOR in the release file.
s.write('.meteor/release', "METEOR@0.9-bad");
run = s.run();
run.matchErr(
"This project says that it uses Meteor 0.9-bad, but");
// No METEOR in the release file.
s.write('.meteor/release', "0.9.x-bad");
run = s.run();
run.matchErr(
"This project says that it uses Meteor 0.9.x-bad, but");
// Non-standard track
s.write('.meteor/release', "FOO@bad");
run = s.run();
run.matchErr(
"This project says that it uses Meteor release FOO@bad, but");
});