Repository: denoland/deno
Pull request: fix(ext/node): add `assert` property to test context object

towards #28837 

This PR adds `assert` property of test context object, which mirrors the APIs from `node:assert`

Changed files:
filename: ext/node/polyfills/testing.ts
patch: @@ -4,10 +4,44 @@ import { primordials } from "ext:core/mod.js";
 const {
   PromisePrototypeThen,
   ArrayPrototypePush,
+  ArrayPrototypeForEach,
   SafePromiseAll,
   SafePromisePrototypeFinally,
 } = primordials;
 import { notImplemented, warnNotImplemented } from "ext:deno_node/_utils.ts";
+import assert from "node:assert";
+
+const methodsToCopy = [
+  "deepEqual",
+  "deepStrictEqual",
+  "doesNotMatch",
+  "doesNotReject",
+  "doesNotThrow",
+  "equal",
+  "fail",
+  "ifError",
+  "match",
+  "notDeepEqual",
+  "notDeepStrictEqual",
+  "notEqual",
+  "notStrictEqual",
+  "partialDeepStrictEqual",
+  "rejects",
+  "strictEqual",
+  "throws",
+];
+
+/** `assert` object available via t.assert */
+let assertObject = undefined;
+function getAssertObject() {
+  if (assertObject === undefined) {
+    assertObject = { __proto__: null };
+    ArrayPrototypeForEach(methodsToCopy, (method) => {
+      assertObject[method] = assert[method];
+    });
+  }
+  return assertObject;
+}
 
 export function run() {
   notImplemented("test.run");
@@ -22,6 +56,10 @@ class NodeTestContext {
     this.#denoContext = t;
   }
 
+  get assert() {
+    return getAssertObject();
+  }
+
   get signal() {
     notImplemented("test.TestContext.signal");
     return null;

filename: tests/specs/node/node_test_module/test.js
patch: @@ -130,6 +130,29 @@ suite("suite", () => {
   });
 });
 
+test("assertions available via text context", async (t) => {
+  assert.strictEqual(t.assert.deepEqual, assert.deepEqual);
+  assert.strictEqual(t.assert.deepStrictEqual, assert.deepStrictEqual);
+  assert.strictEqual(t.assert.doesNotMatch, assert.doesNotMatch);
+  assert.strictEqual(t.assert.doesNotReject, assert.doesNotReject);
+  assert.strictEqual(t.assert.doesNotThrow, assert.doesNotThrow);
+  assert.strictEqual(t.assert.equal, assert.equal);
+  assert.strictEqual(t.assert.fail, assert.fail);
+  assert.strictEqual(t.assert.ifError, assert.ifError);
+  assert.strictEqual(t.assert.match, assert.match);
+  assert.strictEqual(t.assert.notDeepEqual, assert.notDeepEqual);
+  assert.strictEqual(t.assert.notDeepStrictEqual, assert.notDeepStrictEqual);
+  assert.strictEqual(t.assert.notEqual, assert.notEqual);
+  assert.strictEqual(t.assert.notStrictEqual, assert.notStrictEqual);
+  assert.strictEqual(
+    t.assert.partialDeepStrictEqual,
+    assert.partialDeepStrictEqual,
+  );
+  assert.strictEqual(t.assert.rejects, assert.rejects);
+  assert.strictEqual(t.assert.strictEqual, assert.strictEqual);
+  assert.strictEqual(t.assert.throws, assert.throws);
+});
+
 test("unhandled rejection - passes but warns", () => {
   Promise.reject(new Error("rejected from unhandled rejection fail"));
 });

filename: tests/specs/node/node_test_module/test.out
patch: @@ -1,5 +1,5 @@
 [WILDCARD]
-running 66 tests from ./test.js
+running 67 tests from ./test.js
 sync pass todo ...
 ------- output -------
 Warning: Not implemented: test.TestContext.todo
@@ -92,6 +92,7 @@ suite ...
     nested test 2 ... ok ([WILDLINE])
   sub suite 2 ... FAILED (due to 1 failed step) ([WILDLINE])
 suite ... FAILED (due to 3 failed steps) ([WILDLINE])
+assertions available via text context ... ok ([WILDLINE])
 unhandled rejection - passes but warns ...
 Uncaught error from ./test.js FAILED
 unhandled rejection - passes but warns ... cancelled ([WILDCARD])
@@ -227,6 +228,6 @@ suite ... sub suite 1 ... nested test 2 => ./test.js:[WILDLINE]
 suite ... sub suite 2 ... nested test 1 => ./test.js:[WILDLINE]
 ./test.js (uncaught error)
 
-FAILED | 11 passed (21 steps) | 52 failed (5 steps) | 4 ignored [WILDCARD]
+FAILED | 12 passed (21 steps) | 52 failed (5 steps) | 4 ignored [WILDCARD]
 
 error: Test failed