]> git.wincent.com - wincent.git/commitdiff
feat: add color to test harness
authorGreg Hurrell <greg@hurrell.net>
Fri, 20 Mar 2020 16:20:14 +0000 (17:20 +0100)
committerGreg Hurrell <greg@hurrell.net>
Fri, 20 Mar 2020 16:20:14 +0000 (17:20 +0100)
bin/common
install-next
src/__tests__/template-test.ts [moved from src/template-test.ts with 54% similarity]
src/console.ts [new file with mode: 0644]
src/console/COLORS.ts [new file with mode: 0644]
src/harness.ts
src/log.ts [deleted file]
src/main.ts
src/template.ts
src/test.ts

index 8efe6aec8bdf970ef54373e04757c120a2f937bf..5e8bf8feaf5117077d8cfb982e7bf996feb39c94 100755 (executable)
@@ -6,11 +6,17 @@ if [[ "${BASH_SOURCE[0]}" = "${0}" ]]; then
   exit 1
 fi
 
-# https://stackoverflow.com/a/12694189/2103996
-REPO_ROOT="${BASH_SOURCE%/*}/.."
+# Get directory of current script:
+#
+#     https://stackoverflow.com/a/12694189/2103996
 
-# https://stackoverflow.com/a/4045350/2103996
-BIN_DIR=$(cd "$REPO_ROOT/bin" && pwd)
+# and convert to absolute path:
+#
+#     https://stackoverflow.com/a/4045350/2103996
+#
+REPO_ROOT=$(cd "${BASH_SOURCE%/*}/.." && pwd)
+
+BIN_DIR="$REPO_ROOT/bin"
 
 export PATH="$BIN_DIR":$PATH
 
index 43d5a741b5fcabf4342665b1bfd91c406dd6fee9..9634a23c74bb07b4de7ae7603b7f712ad51a0aa5 100755 (executable)
@@ -10,6 +10,10 @@ log_info "Installing TypeScript"
 
 yarn --frozen-lockfile --no-default-rc --no-progress --silent
 
+log_info "Cleaning"
+
+rm -r "$REPO_ROOT"/lib/*
+
 log_info "Building"
 
 tsc
similarity index 54%
rename from src/template-test.ts
rename to src/__tests__/template-test.ts
index c5ed8d87967c7b028f2086fad4c19ae35f670225..0a373e770d6afa79c0acfbe1310de3a5cad82847 100644 (file)
@@ -1,5 +1,5 @@
-import {expect, test} from './harness';
-import template from './template';
+import {expect, test} from '../harness';
+import template from '../template';
 
 test('must be uppercase', () => {
   expect(template('process me')).toBe('PROCESS ME');
diff --git a/src/console.ts b/src/console.ts
new file mode 100644 (file)
index 0000000..fb87be5
--- /dev/null
@@ -0,0 +1,37 @@
+import {clearLine, cursorTo} from 'readline';
+
+export {default as COLORS} from './console/colors';
+
+export function clear() {
+  return new Promise(resolve => {
+    clearLine(process.stderr, 0, () => {
+      cursorTo(process.stderr, 0, undefined, resolve);
+    });
+  });
+}
+
+export function log(...args: Array<any>) {
+  print(...args);
+  print('\n');
+}
+
+export function print(...args: Array<any>) {
+  process.stderr.write(
+    args
+      .map(arg => {
+        try {
+          if (typeof arg === 'object' && arg) {
+            return JSON.stringify(arg, null, 2);
+          } else {
+            return String(arg);
+          }
+        } catch {
+          return '???';
+        }
+      })
+      .join(' '),
+  );
+}
+
+log.clear = clear;
+print.clear = clear;
diff --git a/src/console/COLORS.ts b/src/console/COLORS.ts
new file mode 100644 (file)
index 0000000..1eac6af
--- /dev/null
@@ -0,0 +1,226 @@
+/**
+ * https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
+ * https://stackoverflow.com/a/41407246/2103996
+ */
+const BOLD = '\x1b[1m';
+const GREEN = '\x1b[32m';
+const RED = '\x1b[31m';
+const RESET = '\x1b[0m';
+const REVERSE = '\x1b[7m';
+const YELLOW = '\x1b[33m';
+
+/**
+ * Regular function.
+ *
+ * @overload
+ */
+function bold(input: string): string;
+
+/**
+ * Tagged template literal.
+ *
+ * @overload
+ */
+function bold(
+  input: TemplateStringsArray,
+  ...interpolations: unknown[]
+): string;
+
+function bold(input: any, ...interpolations: unknown[]) {
+  if (Array.isArray(input)) {
+    return style(interpolate(input as any, interpolations), BOLD);
+  } else {
+    return style(input, BOLD);
+  }
+}
+
+/**
+ * Regular function.
+ *
+ * @overload
+ */
+function green(input: string): string;
+
+/**
+ * Tagged template literal.
+ *
+ * @overload
+ */
+function green(
+  input: TemplateStringsArray,
+  ...interpolations: unknown[]
+): string;
+
+function green(input: any, ...interpolations: unknown[]) {
+  if (Array.isArray(input)) {
+    return style(interpolate(input as any, interpolations), GREEN);
+  } else {
+    return style(input, GREEN);
+  }
+}
+
+/**
+ * Regular function.
+ *
+ * @overload
+ */
+function red(input: string): string;
+
+/**
+ * Tagged template literal.
+ *
+ * @overload
+ */
+function red(input: TemplateStringsArray, ...interpolations: unknown[]): string;
+
+function red(input: any, ...interpolations: unknown[]) {
+  if (Array.isArray(input)) {
+    return style(interpolate(input as any, interpolations), RED);
+  } else {
+    return style(input, RED);
+  }
+}
+
+/**
+ * Regular function.
+ *
+ * @overload
+ */
+function reverse(input: string): string;
+
+/**
+ * Tagged template literal.
+ *
+ * @overload
+ */
+function reverse(
+  input: TemplateStringsArray,
+  ...interpolations: unknown[]
+): string;
+
+function reverse(input: any, ...interpolations: unknown[]) {
+  if (Array.isArray(input)) {
+    return style(interpolate(input as any, interpolations), REVERSE);
+  } else {
+    return style(input, REVERSE);
+  }
+}
+
+/**
+ * Regular function.
+ *
+ * @overload
+ */
+function yellow(input: string): string;
+
+/**
+ * Tagged template literal.
+ *
+ * @overload
+ */
+function yellow(
+  input: TemplateStringsArray,
+  ...interpolations: unknown[]
+): string;
+
+function yellow(input: any, ...interpolations: unknown[]) {
+  if (Array.isArray(input)) {
+    return style(interpolate(input as any, interpolations), YELLOW);
+  } else {
+    return style(input, YELLOW);
+  }
+}
+
+function style(text: string, escape: string) {
+  return `${escape}${text}${RESET}`;
+}
+
+function interpolate(strings: TemplateStringsArray, interpolations: unknown[]) {
+  return strings.reduce((acc, string, i) => {
+    if (i < interpolations.length) {
+      return acc + string + String(interpolations[i]);
+    } else {
+      return acc + string;
+    }
+  }, '');
+}
+
+const COLORS = {
+  bold(
+    this: unknown,
+    strings: TemplateStringsArray,
+    ...interpolations: unknown[]
+  ): string {
+    const result = bold(strings, ...interpolations);
+
+    if (typeof this === 'function') {
+      return this.call(null, result);
+    } else {
+      return result;
+    }
+  },
+
+  green(
+    this: unknown,
+    strings: TemplateStringsArray,
+    ...interpolations: unknown[]
+  ): string {
+    const result = green(strings, ...interpolations);
+
+    if (typeof this === 'function') {
+      return this.call(null, result);
+    } else {
+      return result;
+    }
+  },
+
+  red(
+    this: unknown,
+    strings: TemplateStringsArray,
+    ...interpolations: unknown[]
+  ): string {
+    const result = red(strings, ...interpolations);
+
+    if (typeof this === 'function') {
+      return this.call(null, result);
+    } else {
+      return result;
+    }
+  },
+
+  reverse(
+    this: unknown,
+    strings: TemplateStringsArray,
+    ...interpolations: unknown[]
+  ): string {
+    const result = reverse(strings, ...interpolations);
+
+    if (typeof this === 'function') {
+      return this.call(null, result);
+    } else {
+      return result;
+    }
+  },
+
+  yellow(
+    this: unknown,
+    strings: TemplateStringsArray,
+    ...interpolations: unknown[]
+  ): string {
+    const result = yellow(strings, ...interpolations);
+
+    if (typeof this === 'function') {
+      return this.call(null, result);
+    } else {
+      return result;
+    }
+  },
+};
+
+export default {
+  bold: Object.assign(COLORS.bold, COLORS),
+  green: Object.assign(COLORS.green, COLORS),
+  red: Object.assign(COLORS.red, COLORS),
+  reverse: Object.assign(COLORS.reverse, COLORS),
+  yellow: Object.assign(COLORS.yellow, COLORS),
+};
index cae4f6982c93bb5a59bbb3075f2a2723edb26abf..ebfe536137fca5e9df68eaf81f6194c159fd2c66 100644 (file)
@@ -1,6 +1,10 @@
 import * as assert from 'assert';
 
-const TESTS: Array<[string, () => void]> = [];
+import {COLORS, log, print} from './console';
+
+const {green, red, yellow} = COLORS;
+
+const TESTS: Array<[string, () => void | Promise<void>]> = [];
 
 export function expect(value: unknown) {
   return {
@@ -14,20 +18,31 @@ export function test(description: string, callback: () => void): void {
   TESTS.push([description, callback]);
 }
 
-export function run() {
-  const errors = [];
+export async function run() {
+  let failureCount = 0;
+  let successCount = 0;
 
-  TESTS.forEach(([description, callback]) => {
+  for (const [description, callback] of TESTS) {
     try {
-      console.log(description);
-      callback();
+      print(yellow.reverse` TEST `, description);
+      await callback();
+      successCount++;
+      await print.clear();
+      log(green.reverse` PASS `, description);
     } catch (error) {
-      console.log(error);
-      errors.push(error);
+      await print.clear();
+      log(red.reverse` FAIL `, description);
     }
-  });
+  }
+
+  log();
+  log(
+    green.bold`${successCount} passed` + ',',
+    red.bold`${failureCount} failed` + ',',
+    `${successCount + failureCount} total`,
+  );
 
-  if (errors.length) {
-    // bail...
+  if (failureCount) {
+    process.exit(1);
   }
 }
diff --git a/src/log.ts b/src/log.ts
deleted file mode 100644 (file)
index 999959a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function log(...args: Array<any>) {
-  console.log(...args);
-}
index 9a3856ceb35198009190c7ba9f725edafdb5c0bd..3204731e3a3edc130914bfe4dd15de51fcb235b4 100644 (file)
@@ -1,4 +1,4 @@
-import log from './log';
+import {log} from './console';
 import test from './test';
 
 // argv[0] = node executable
index 31f613451ff2519fc09cce683a03bb0dd0e9c9dd..298429c6556b55b929e21953ddedda20606353fd 100644 (file)
@@ -1,3 +1,11 @@
+/**
+ * @see https://en.wikipedia.org/wiki/Jinja_(template_engine)
+ * @see https://jinja.palletsprojects.com/
+ */
 export default function template(input: string) {
+  // {% %} statement
+  // {{ }} expression
+  // {# #} comment
+  // # ## line statements
   return input.toUpperCase();
 }
index 3ae2e44049f244549895c85f06572cf0effe9dcc..4af7fc57ee41e1eea62083c1825e69902d99ca83 100644 (file)
@@ -15,8 +15,6 @@ export default async function test() {
       } catch (error) {
         console.log('caught', error);
       }
-
-      // print results
     }
   }