Repository: unexpectedjs/unexpected
Pull request #674 (2019-12-17 22:55:08)

Add support for BigInt
Add a `BigInt` type and support it in the `<number>` assertions where it makes sense.

Context: https://gitter.im/unexpectedjs/unexpected?at=5df92bfd0dc628523e51d066


Patch:
diff --git a/lib/assertions.js b/lib/assertions.js
index 4f4b973f..1234351b 100644
--- a/lib/assertions.js
+++ b/lib/assertions.js
@@ -932,20 +932,11 @@ module.exports = expect => {
   });
 
   expect.addAssertion(
-    '<number> [not] to be within <number> <number>',
-    (expect, subject, start, finish) => {
-      expect.argsOutput = output => {
-        output
-          .appendInspected(start)
-          .text('..')
-          .appendInspected(finish);
-      };
-      expect(subject >= start && subject <= finish, '[not] to be truthy');
-    }
-  );
-
-  expect.addAssertion(
-    '<string> [not] to be within <string> <string>',
+    [
+      '<number> [not] to be within <number> <number>',
+      '<BigInt> [not] to be within <BigInt> <BigInt>',
+      '<string> [not] to be within <string> <string>'
+    ],
     (expect, subject, start, finish) => {
       expect.argsOutput = output => {
         output
@@ -958,7 +949,11 @@ module.exports = expect => {
   );
 
   expect.addAssertion(
-    '<number> [not] to be (less than|below) <number>',
+    [
+      '<number> [not] to be (less than|below) <number>',
+      '<BigInt> [not] to be (less than|below) <BigInt>',
+      '<string> [not] to be (less than|below) <string>'
+    ],
     (expect, subject, value) => {
       expect(subject < value, '[not] to be truthy');
     }
@@ -972,42 +967,33 @@ module.exports = expect => {
   );
 
   expect.addAssertion(
-    '<number> [not] to be less than or equal to <number>',
-    (expect, subject, value) => {
-      expect(subject <= value, '[not] to be truthy');
-    }
-  );
-
-  expect.addAssertion(
-    '<string> [not] to be less than or equal to <string>',
+    [
+      '<number> [not] to be less than or equal to <number>',
+      '<BigInt> [not] to be less than or equal to <BigInt>',
+      '<string> [not] to be less than or equal to <string>'
+    ],
     (expect, subject, value) => {
       expect(subject <= value, '[not] to be truthy');
     }
   );
 
   expect.addAssertion(
-    '<number> [not] to be (greater than|above) <number>',
-    (expect, subject, value) => {
-      expect(subject > value, '[not] to be truthy');
-    }
-  );
-
-  expect.addAssertion(
-    '<string> [not] to be (greater than|above) <string>',
+    [
+      '<number> [not] to be (greater than|above) <number>',
+      '<BigInt> [not] to be (greater than|above) <BigInt>',
+      '<string> [not] to be (greater than|above) <string>'
+    ],
     (expect, subject, value) => {
       expect(subject > value, '[not] to be truthy');
     }
   );
 
   expect.addAssertion(
-    '<number> [not] to be greater than or equal to <number>',
-    (expect, subject, value) => {
-      expect(subject >= value, '[not] to be truthy');
-    }
-  );
-
-  expect.addAssertion(
-    '<string> [not] to be greater than or equal to <string>',
+    [
+      '<number> [not] to be greater than or equal to <number>',
+      '<BigInt> [not] to be greater than or equal to <BigInt>',
+      '<string> [not] to be greater than or equal to <string>'
+    ],
     (expect, subject, value) => {
       expect(subject >= value, '[not] to be truthy');
     }
@@ -1017,10 +1003,18 @@ module.exports = expect => {
     expect(subject, '[not] to be greater than', 0);
   });
 
+  expect.addAssertion('<BigInt> [not] to be positive', (expect, subject) => {
+    expect(subject > 0, '[not] to be true');
+  });
+
   expect.addAssertion('<number> [not] to be negative', (expect, subject) => {
     expect(subject, '[not] to be less than', 0);
   });
 
+  expect.addAssertion('<BigInt> [not] to be negative', (expect, subject) => {
+    expect(subject < 0, '[not] to be true');
+  });
+
   expect.addAssertion('<any> to equal <any>', (expect, subject, value) => {
     expect.withError(
       () => {
diff --git a/lib/types.js b/lib/types.js
index 23eb9073..9f34c34e 100644
--- a/lib/types.js
+++ b/lib/types.js
@@ -1327,6 +1327,19 @@ module.exports = function(expect) {
     }
   });
 
+  expect.addType({
+    name: 'BigInt',
+    identify(value) {
+      return typeof value === 'bigint';
+    },
+    inspect(value, depth, output) {
+      return output
+        .code('BigInt(', 'javascript')
+        .jsNumber(value.toString())
+        .code(')', 'javascript');
+    }
+  });
+
   expect.addType({
     name: 'boolean',
     identify(value) {


Test patch:
diff --git a/test/api/shift.spec.js b/test/api/shift.spec.js
index fad8d873..d3c7d27b 100644
--- a/test/api/shift.spec.js
+++ b/test/api/shift.spec.js
@@ -374,6 +374,7 @@ describe('expect.shift', () => {
         '    The assertion does not have a matching signature for:\n' +
         '      <string> to be negative\n' +
         '    did you mean:\n' +
+        '      <BigInt> [not] to be negative\n' +
         '      <number> [not] to be negative'
     );
   });
@@ -401,6 +402,7 @@ describe('expect.shift', () => {
           '    The assertion does not have a matching signature for:\n' +
           '      <string> to be negative\n' +
           '    did you mean:\n' +
+          '      <BigInt> [not] to be negative\n' +
           '      <number> [not] to be negative'
       );
     });
diff --git a/test/assertions/to-be-NaN.spec.js b/test/assertions/to-be-NaN.spec.js
index 4ff8c02f..03fb09f8 100644
--- a/test/assertions/to-be-NaN.spec.js
+++ b/test/assertions/to-be-NaN.spec.js
@@ -1,6 +1,6 @@
 /* global expect */
 describe('to be NaN assertion', () => {
-  it('asserts that the value is NaN or not', () => {
+  it('assert that the value is NaN or not', () => {
     expect(NaN, 'to be NaN');
     expect(2, 'not to be NaN');
   });
diff --git a/test/assertions/to-be-falsy.spec.js b/test/assertions/to-be-falsy.spec.js
index 34760b97..7d499851 100644
--- a/test/assertions/to-be-falsy.spec.js
+++ b/test/assertions/to-be-falsy.spec.js
@@ -1,6 +1,6 @@
 /* global expect */
 describe('to be falsy assertion', () => {
-  it('asserts that the value is falsy', () => {
+  it('assert that the value is falsy', () => {
     expect(true, 'not to be falsy');
     expect(0, 'to be falsy');
     expect(null, 'to be falsy');
diff --git a/test/assertions/to-be-greater-than-or-equal.spec.js b/test/assertions/to-be-greater-than-or-equal.spec.js
index 3385d73a..93a07c5d 100644
--- a/test/assertions/to-be-greater-than-or-equal.spec.js
+++ b/test/assertions/to-be-greater-than-or-equal.spec.js
@@ -1,4 +1,4 @@
-/* global expect */
+/* global expect, BigInt */
 describe('greater than or equal assertion', () => {
   it('asserts >=', () => {
     expect(3, 'to be greater than or equal to', 2);
@@ -16,4 +16,39 @@ describe('greater than or equal assertion', () => {
       'expected -1 to be greater than or equal to 0'
     );
   });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts >=', () => {
+        expect(BigInt(3), 'to be greater than or equal to', BigInt(2));
+        expect(BigInt(2), 'to be greater than or equal to', BigInt(2));
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(-1), 'to be greater than or equal to', BigInt(0));
+          },
+          'to throw exception',
+          'expected BigInt(-1) to be greater than or equal to BigInt(0)'
+        );
+      });
+
+      it('refuses to compare a BigInt to a number', () => {
+        expect(
+          function() {
+            expect(BigInt(-1), 'to be greater than or equal to', 0);
+          },
+          'to throw',
+          'expected BigInt(-1) to be greater than or equal to 0\n' +
+            '  The assertion does not have a matching signature for:\n' +
+            '    <BigInt> to be greater than or equal to <number>\n' +
+            '  did you mean:\n' +
+            '    <BigInt> [not] to be greater than or equal to <BigInt>\n' +
+            '    <number> [not] to be greater than or equal to <number>\n' +
+            '    <string> [not] to be greater than or equal to <string>'
+        );
+      });
+    });
+  }
 });
diff --git a/test/assertions/to-be-greater-than.spec.js b/test/assertions/to-be-greater-than.spec.js
index 15cce4a6..cad075a9 100644
--- a/test/assertions/to-be-greater-than.spec.js
+++ b/test/assertions/to-be-greater-than.spec.js
@@ -1,4 +1,4 @@
-/* global expect */
+/* global expect, BigInt */
 describe('greater than assertion', () => {
   it('assert >', () => {
     expect(3, 'to be greater than', 2);
@@ -26,8 +26,44 @@ describe('greater than assertion', () => {
         '  The assertion does not have a matching signature for:\n' +
         '    <NaN> not to be greater than <number>\n' +
         '  did you mean:\n' +
+        '    <BigInt> [not] to be (greater than|above) <BigInt>\n' +
         '    <number> [not] to be (greater than|above) <number>\n' +
         '    <string> [not] to be (greater than|above) <string>'
     );
   });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts >', () => {
+        expect(BigInt(3), 'to be greater than', BigInt(2));
+        expect(BigInt(1), 'to be above', BigInt(0));
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(0), 'to be greater than', BigInt(0));
+          },
+          'to throw exception',
+          'expected BigInt(0) to be greater than BigInt(0)'
+        );
+      });
+
+      it('refuses to compare a BigInt to a number', () => {
+        expect(
+          function() {
+            expect(BigInt(123), 'to be greater than', 1);
+          },
+          'to throw',
+          'expected BigInt(123) to be greater than 1\n' +
+            '  The assertion does not have a matching signature for:\n' +
+            '    <BigInt> to be greater than <number>\n' +
+            '  did you mean:\n' +
+            '    <BigInt> [not] to be (greater than|above) <BigInt>\n' +
+            '    <number> [not] to be (greater than|above) <number>\n' +
+            '    <string> [not] to be (greater than|above) <string>'
+        );
+      });
+    });
+  }
 });
diff --git a/test/assertions/to-be-less-than-or-equal.spec.js b/test/assertions/to-be-less-than-or-equal.spec.js
index 7806ff83..95efaab4 100644
--- a/test/assertions/to-be-less-than-or-equal.spec.js
+++ b/test/assertions/to-be-less-than-or-equal.spec.js
@@ -1,4 +1,4 @@
-/* global expect */
+/* global expect, BigInt */
 describe('less than or equal assertion', () => {
   it('asserts <=', () => {
     expect(0, 'to be less than or equal to', 4);
@@ -16,4 +16,39 @@ describe('less than or equal assertion', () => {
       'expected 0 to be less than or equal to -1'
     );
   });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts <=', () => {
+        expect(BigInt(0), 'to be less than or equal to', BigInt(4));
+        expect(BigInt(0), 'to be less than or equal to', BigInt(0));
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(0), 'to be less than or equal to', BigInt(-1));
+          },
+          'to throw exception',
+          'expected BigInt(0) to be less than or equal to BigInt(-1)'
+        );
+      });
+
+      it('refuses to compare a BigInt to a number', () => {
+        expect(
+          function() {
+            expect(BigInt(123), 'to be less than or equal to', 1);
+          },
+          'to throw',
+          'expected BigInt(123) to be less than or equal to 1\n' +
+            '  The assertion does not have a matching signature for:\n' +
+            '    <BigInt> to be less than or equal to <number>\n' +
+            '  did you mean:\n' +
+            '    <BigInt> [not] to be less than or equal to <BigInt>\n' +
+            '    <number> [not] to be less than or equal to <number>\n' +
+            '    <string> [not] to be less than or equal to <string>'
+        );
+      });
+    });
+  }
 });
diff --git a/test/assertions/to-be-less-than.spec.js b/test/assertions/to-be-less-than.spec.js
new file mode 100644
index 00000000..d36430b9
--- /dev/null
+++ b/test/assertions/to-be-less-than.spec.js
@@ -0,0 +1,53 @@
+/* global expect, BigInt */
+describe('less than assertion', () => {
+  it('asserts <', () => {
+    expect(0, 'to be less than', 4);
+    expect(0, 'to be below', 1);
+    expect('a', 'to be less than', 'b');
+  });
+
+  it('throws when the assertion fails', () => {
+    expect(
+      function() {
+        expect(0, 'to be less than', 0);
+      },
+      'to throw exception',
+      'expected 0 to be less than 0'
+    );
+  });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts <', () => {
+        expect(BigInt(0), 'to be less than', BigInt(4));
+        expect(BigInt(0), 'to be below', BigInt(1));
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(0), 'to be less than', BigInt(0));
+          },
+          'to throw exception',
+          'expected BigInt(0) to be less than BigInt(0)'
+        );
+      });
+
+      it('refuses to compare to a number', () => {
+        expect(
+          function() {
+            expect(BigInt(0), 'to be less than', 1);
+          },
+          'to throw exception',
+          'expected BigInt(0) to be less than 1\n' +
+            '  The assertion does not have a matching signature for:\n' +
+            '    <BigInt> to be less than <number>\n' +
+            '  did you mean:\n' +
+            '    <BigInt> [not] to be (less than|below) <BigInt>\n' +
+            '    <number> [not] to be (less than|below) <number>\n' +
+            '    <string> [not] to be (less than|below) <string>'
+        );
+      });
+    });
+  }
+});
diff --git a/test/assertions/to-be-less-then.spec.js b/test/assertions/to-be-less-then.spec.js
deleted file mode 100644
index 6cf29169..00000000
--- a/test/assertions/to-be-less-then.spec.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/* global expect */
-describe('less than assertion', () => {
-  it('asserts <', () => {
-    expect(0, 'to be less than', 4);
-    expect(0, 'to be below', 1);
-    expect('a', 'to be less than', 'b');
-  });
-
-  it('throws when the assertion fails', () => {
-    expect(
-      function() {
-        expect(0, 'to be less than', 0);
-      },
-      'to throw exception',
-      'expected 0 to be less than 0'
-    );
-  });
-});
diff --git a/test/assertions/to-be-negative.spec.js b/test/assertions/to-be-negative.spec.js
index 15db7c2e..53e11583 100644
--- a/test/assertions/to-be-negative.spec.js
+++ b/test/assertions/to-be-negative.spec.js
@@ -1,6 +1,6 @@
-/* global expect */
+/* global expect, BigInt */
 describe('negative assertion', () => {
-  it('assert that a number is negative', () => {
+  it('asserts that a number is negative', () => {
     expect(-1, 'to be negative');
   });
 
@@ -13,4 +13,22 @@ describe('negative assertion', () => {
       'expected 0 to be negative'
     );
   });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts that a number is negative', () => {
+        expect(BigInt(-1), 'to be negative');
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(0), 'to be negative');
+          },
+          'to throw exception',
+          'expected BigInt(0) to be negative'
+        );
+      });
+    });
+  }
 });
diff --git a/test/assertions/to-be-positive.spec.js b/test/assertions/to-be-positive.spec.js
index 0ae7f5fb..89103a9c 100644
--- a/test/assertions/to-be-positive.spec.js
+++ b/test/assertions/to-be-positive.spec.js
@@ -1,6 +1,6 @@
-/* global expect */
+/* global expect, BigInt */
 describe('positive assertion', () => {
-  it('assert that a number is positive', () => {
+  it('asserts that a number is positive', () => {
     expect(3, 'to be positive');
   });
 
@@ -13,4 +13,22 @@ describe('positive assertion', () => {
       'expected 0 to be positive'
     );
   });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts that a number is positive', () => {
+        expect(BigInt(3), 'to be positive');
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(0), 'to be positive');
+          },
+          'to throw exception',
+          'expected BigInt(0) to be positive'
+        );
+      });
+    });
+  }
 });
diff --git a/test/assertions/to-be-truthy.spec.js b/test/assertions/to-be-truthy.spec.js
index 2851febb..72295cb3 100644
--- a/test/assertions/to-be-truthy.spec.js
+++ b/test/assertions/to-be-truthy.spec.js
@@ -1,6 +1,6 @@
 /* global expect */
 describe('to be ok/truthy assertion', () => {
-  it('asserts that the value is truthy', () => {
+  it('assert that the value is truthy', () => {
     expect(1, 'to be ok');
     expect(true, 'to be ok');
     expect({}, 'to be truthy');
diff --git a/test/assertions/to-be-within.spec.js b/test/assertions/to-be-within.spec.js
index dfe29da6..374ebee4 100644
--- a/test/assertions/to-be-within.spec.js
+++ b/test/assertions/to-be-within.spec.js
@@ -1,4 +1,4 @@
-/* global expect */
+/* global expect, BigInt */
 describe('within assertion', () => {
   it('asserts a number within a range', () => {
     expect(0, 'to be within', 0, 4);
@@ -27,6 +27,7 @@ describe('within assertion', () => {
         '  The assertion does not have a matching signature for:\n' +
         '    <null> not to be within <number> <number>\n' +
         '  did you mean:\n' +
+        '    <BigInt> [not] to be within <BigInt> <BigInt>\n' +
         '    <number> [not] to be within <number> <number>\n' +
         '    <string> [not] to be within <string> <string>'
     );
@@ -41,4 +42,40 @@ describe('within assertion', () => {
       "expected 'a' to be within 'c'..'d'"
     );
   });
+
+  if (typeof BigInt === 'function') {
+    describe('with BigInt', function() {
+      it('asserts a number within a range', () => {
+        expect(BigInt(0), 'to be within', BigInt(0), BigInt(4));
+        expect(BigInt(1), 'to be within', BigInt(0), BigInt(4));
+        expect(BigInt(4), 'to be within', BigInt(0), BigInt(4));
+      });
+
+      it('throws when the assertion fails', () => {
+        expect(
+          function() {
+            expect(BigInt(4), 'not to be within', BigInt(0), BigInt(4));
+          },
+          'to throw exception',
+          'expected BigInt(4) not to be within BigInt(0)..BigInt(4)'
+        );
+      });
+
+      it('refuses to compare a BigInt to a number', () => {
+        expect(
+          function() {
+            expect(BigInt(123), 'to be within', 100, 200);
+          },
+          'to throw',
+          'expected BigInt(123) to be within 100, 200\n' +
+            '  The assertion does not have a matching signature for:\n' +
+            '    <BigInt> to be within <number> <number>\n' +
+            '  did you mean:\n' +
+            '    <BigInt> [not] to be within <BigInt> <BigInt>\n' +
+            '    <number> [not] to be within <number> <number>\n' +
+            '    <string> [not] to be within <string> <string>'
+        );
+      });
+    });
+  }
 });
diff --git a/test/types/BigInt-type.spec.js b/test/types/BigInt-type.spec.js
new file mode 100644
index 00000000..1b0c0420
--- /dev/null
+++ b/test/types/BigInt-type.spec.js
@@ -0,0 +1,29 @@
+/* global expect, BigInt */
+
+// Bogus test to prevent jest from failing with "Your test suite must contain at least one test"
+// on node.js 9 and below, which don't have BigInt support:
+it('should be registered as a type', function() {
+  expect(expect.getType('BigInt'), 'to be truthy');
+});
+
+if (typeof BigInt === 'function') {
+  describe('BigInt type', () => {
+    it('should consider an instance equal to itself', () => {
+      const a = BigInt(1);
+      expect(a, 'to equal', a);
+    });
+
+    it('should not be considered equal to the equivalent number', () => {
+      expect(
+        () => expect(BigInt(1), 'to equal', 1),
+        'to throw',
+        'expected BigInt(1) to equal 1'
+      );
+    });
+
+    it('should inspect as source code', () => {
+      const a = BigInt(123);
+      expect(a, 'to inspect as', 'BigInt(123)');
+    });
+  });
+}