From 71dd46c5fb1508f8991ed82de358c257534884dc Mon Sep 17 00:00:00 2001
From: Daniel Mantovani <dmanto@gmail.com>
Date: Mon, 11 Nov 2024 12:45:04 -0300
Subject: [PATCH 1/2] fix ua asserts issue without tap

---
 src/user-agent/test.ts  |  10 +-
 test/test-user-agent.js | 463 +++++++++++++++++++++-------------------
 2 files changed, 256 insertions(+), 217 deletions(-)

diff --git a/src/user-agent/test.ts b/src/user-agent/test.ts
index e0fa9e28..2091aea8 100644
--- a/src/user-agent/test.ts
+++ b/src/user-agent/test.ts
@@ -46,7 +46,15 @@ export class TestUserAgent extends MockUserAgent {
    * Delegate assertion to test framework currently in use.
    */
   assert(name: string, args: any[], msg: string, skip: SkipFunction): void {
-    const test: any = this._assert ?? assert;
+    const test: any = this._assert ?? {
+      equal: assert.strictEqual,
+      not: assert.notStrictEqual,
+      match: assert.match,
+      notMatch: assert.doesNotMatch,
+      ok: assert.ok,
+      notOk: (value: any, message: string) => assert.ok(!value, message),
+      same: assert.deepStrictEqual
+    };
     test[name](...args, msg, {stack: this._stack.captureString(10, skip).replaceAll('file://', '')});
   }
 
diff --git a/test/test-user-agent.js b/test/test-user-agent.js
index fbda7a98..b338b00c 100644
--- a/test/test-user-agent.js
+++ b/test/test-user-agent.js
@@ -28,240 +28,271 @@ t.test('TestUserAgent', async t => {
 
   app.get('/target', ctx => ctx.render({text: 'Hi'}));
 
-  const ua = await app.newTestUserAgent({tap: t});
+  for (const useTap of [true, false]) {
+    const ua = useTap ? await app.newTestUserAgent({tap: t}) : await app.newTestUserAgent();
 
-  await t.test('Hello World', async () => {
-    (await ua.getOk('/')).statusIs(200).bodyIs('Hello Mojo!');
-  });
+    await t.test('Status Assertion test', async () => {
+      (await ua.getOk('/')).statusIs(200);
+    });
 
-  await t.test('Methods', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    await ua.deleteOk('/');
-    await ua.getOk('/');
-    await ua.headOk('/');
-    await ua.optionsOk('/');
-    await ua.patchOk('/');
-    await ua.postOk('/');
-    await ua.putOk('/');
-
-    t.same(results, [
-      ['ok', [true], 'DELETE request for /'],
-      ['ok', [true], 'GET request for /'],
-      ['ok', [true], 'HEAD request for /'],
-      ['ok', [true], 'OPTIONS request for /'],
-      ['ok', [true], 'PATCH request for /'],
-      ['ok', [true], 'POST request for /'],
-      ['ok', [true], 'PUT request for /']
-    ]);
-  });
+    await t.test('Header Assertion tests', async () => {
+      (await ua.getOk('/'))
+        .typeIs('text/plain; charset=utf-8')
+        .typeLike(/plain/)
+        .headerIs('Content-Type', 'text/plain; charset=utf-8')
+        .headerLike('Content-Type', /plain/)
+        .headerExists('Content-Type')
+        .headerExistsNot('X-Test');
+    });
 
-  await t.test('Status', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
+    await t.test('Body Assertion tests', async () => {
+      (await ua.getOk('/')).bodyIs('Hello Mojo!').bodyLike(/Hello/).bodyUnlike(/Bye/);
+    });
 
-    (await ua.getOk('/')).statusIs(200);
+    await t.test('JSON Assertion tests', async () => {
+      (await ua.getOk('/index.json')).jsonIs({a: ['b', 'c']}).jsonIs(['b', 'c'], '/a');
+    });
 
-    t.same(results, [
-      ['ok', [true], 'GET request for /'],
-      ['equal', [200, 200], 'response status is 200']
-    ]);
-  });
+    await t.test('YAML Assertion tests', async () => {
+      (await ua.getOk('/index.yaml')).yamlIs({a: ['b', 'c']}).yamlIs(['b', 'c'], '/a');
+    });
 
-  await t.test('Headers', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    (await ua.getOk('/'))
-      .typeIs('text/plain')
-      .typeLike(/plain/)
-      .headerExists('Content-Type')
-      .headerExistsNot('Content-Disposition')
-      .headerIs('Content-Type', 'text/plain')
-      .headerIsnt('Content-Type', 'text/plain')
-      .headerLike('Content-Type', /plain/);
-
-    t.same(results, [
-      ['ok', [true], 'GET request for /'],
-      ['equal', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
-      ['match', ['text/plain; charset=utf-8', /plain/], 'Content-Type is similar'],
-      ['ok', [true], 'header "Content-Type" exists'],
-      ['notOk', [false], 'no "Content-Disposition" header'],
-      ['equal', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
-      ['not', ['text/plain; charset=utf-8', 'text/plain'], 'not Content-Type: text/plain'],
-      ['match', ['text/plain; charset=utf-8', /plain/], 'Content-Type is similar']
-    ]);
-  });
+    await t.test('HTML Assertion tests', async () => {
+      (await ua.getOk('/first.html'))
+        .elementExists('p')
+        .elementExistsNot('body #error')
+        .textLike('p', /One/)
+        .textUnlike('p', /Zero/);
+    });
 
-  await t.test('Body', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    (await ua.getOk('/'))
-      .bodyIs('test')
-      .bodyIsnt('test')
-      .bodyLike(/that/)
-      .bodyUnlike(/whatever/);
-
-    t.same(results, [
-      ['ok', [true], 'GET request for /'],
-      ['equal', ['Hello Mojo!', 'test'], 'body is equal'],
-      ['not', ['Hello Mojo!', 'test'], 'body is not equal'],
-      ['match', ['Hello Mojo!', /that/], 'body is similar'],
-      ['notMatch', ['Hello Mojo!', /whatever/], 'body is not similar']
-    ]);
-  });
+    await t.test('Methods', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      await ua.deleteOk('/');
+      await ua.getOk('/');
+      await ua.headOk('/');
+      await ua.optionsOk('/');
+      await ua.patchOk('/');
+      await ua.postOk('/');
+      await ua.putOk('/');
+
+      t.same(results, [
+        ['ok', [true], 'DELETE request for /'],
+        ['ok', [true], 'GET request for /'],
+        ['ok', [true], 'HEAD request for /'],
+        ['ok', [true], 'OPTIONS request for /'],
+        ['ok', [true], 'PATCH request for /'],
+        ['ok', [true], 'POST request for /'],
+        ['ok', [true], 'PUT request for /']
+      ]);
+    });
 
-  await t.test('HTML', async t => {
-    let results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    (await ua.getOk('/first.html'))
-      .elementExists('p')
-      .elementExists('h1')
-      .elementExistsNot('h1')
-      .elementExistsNot('div')
-      .textLike('div', /test/)
-      .textLike('nothing', /123/)
-      .textUnlike('div', /test/)
-      .textUnlike('nothing', /123/);
-
-    t.same(results, [
-      ['ok', [true], 'GET request for /first.html'],
-      ['ok', [true], 'element for selector "p" exists'],
-      ['ok', [false], 'element for selector "h1" exists'],
-      ['ok', [true], 'no element for selector "h1"'],
-      ['ok', [false], 'no element for selector "div"'],
-      ['match', ['Two', /test/], 'similar match for selector "div"'],
-      ['match', ['', /123/], 'similar match for selector "nothing"'],
-      ['notMatch', ['Two', /test/], 'no similar match for selector "div"'],
-      ['notMatch', ['', /123/], 'no similar match for selector "nothing"']
-    ]);
-
-    results = [];
-    (await ua.getOk('/second.html')).elementExists('b').elementExistsNot('div');
-
-    t.same(results, [
-      ['ok', [true], 'GET request for /second.html'],
-      ['ok', [true], 'element for selector "b" exists'],
-      ['ok', [true], 'no element for selector "div"']
-    ]);
-  });
+    await t.test('Status', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      (await ua.getOk('/')).statusIs(200);
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /'],
+        ['equal', [200, 200], 'response status is 200']
+      ]);
+    });
+
+    await t.test('Headers', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      (await ua.getOk('/'))
+        .typeIs('text/plain')
+        .typeLike(/plain/)
+        .headerExists('Content-Type')
+        .headerExistsNot('Content-Disposition')
+        .headerIs('Content-Type', 'text/plain')
+        .headerIsnt('Content-Type', 'text/plain')
+        .headerLike('Content-Type', /plain/);
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /'],
+        ['equal', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
+        ['match', ['text/plain; charset=utf-8', /plain/], 'Content-Type is similar'],
+        ['ok', [true], 'header "Content-Type" exists'],
+        ['notOk', [false], 'no "Content-Disposition" header'],
+        ['equal', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
+        ['not', ['text/plain; charset=utf-8', 'text/plain'], 'not Content-Type: text/plain'],
+        ['match', ['text/plain; charset=utf-8', /plain/], 'Content-Type is similar']
+      ]);
+    });
 
-  await t.test('JSON', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    (await ua.getOk('/index.json'))
-      .jsonHas('/a')
-      .jsonHas('/d')
-      .jsonIs(['b', 'c'], '/a')
-      .jsonIs('e', '/d')
-      .jsonIs({a: ['b', 'c']});
-
-    t.same(results, [
-      ['ok', [true], 'GET request for /index.json'],
-      ['ok', [true], 'has value for JSON Pointer "/a" (JSON)'],
-      ['ok', [false], 'has value for JSON Pointer "/d" (JSON)'],
-      [
-        'same',
+    await t.test('Body', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      (await ua.getOk('/'))
+        .bodyIs('test')
+        .bodyIsnt('test')
+        .bodyLike(/that/)
+        .bodyUnlike(/whatever/);
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /'],
+        ['equal', ['Hello Mojo!', 'test'], 'body is equal'],
+        ['not', ['Hello Mojo!', 'test'], 'body is not equal'],
+        ['match', ['Hello Mojo!', /that/], 'body is similar'],
+        ['notMatch', ['Hello Mojo!', /whatever/], 'body is not similar']
+      ]);
+    });
+
+    await t.test('HTML', async t => {
+      let results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      (await ua.getOk('/first.html'))
+        .elementExists('p')
+        .elementExists('h1')
+        .elementExistsNot('h1')
+        .elementExistsNot('div')
+        .textLike('div', /test/)
+        .textLike('nothing', /123/)
+        .textUnlike('div', /test/)
+        .textUnlike('nothing', /123/);
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /first.html'],
+        ['ok', [true], 'element for selector "p" exists'],
+        ['ok', [false], 'element for selector "h1" exists'],
+        ['ok', [true], 'no element for selector "h1"'],
+        ['ok', [false], 'no element for selector "div"'],
+        ['match', ['Two', /test/], 'similar match for selector "div"'],
+        ['match', ['', /123/], 'similar match for selector "nothing"'],
+        ['notMatch', ['Two', /test/], 'no similar match for selector "div"'],
+        ['notMatch', ['', /123/], 'no similar match for selector "nothing"']
+      ]);
+
+      results = [];
+      (await ua.getOk('/second.html')).elementExists('b').elementExistsNot('div');
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /second.html'],
+        ['ok', [true], 'element for selector "b" exists'],
+        ['ok', [true], 'no element for selector "div"']
+      ]);
+    });
+
+    await t.test('JSON', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      (await ua.getOk('/index.json'))
+        .jsonHas('/a')
+        .jsonHas('/d')
+        .jsonIs(['b', 'c'], '/a')
+        .jsonIs('e', '/d')
+        .jsonIs({a: ['b', 'c']});
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /index.json'],
+        ['ok', [true], 'has value for JSON Pointer "/a" (JSON)'],
+        ['ok', [false], 'has value for JSON Pointer "/d" (JSON)'],
         [
-          ['b', 'c'],
-          ['b', 'c']
+          'same',
+          [
+            ['b', 'c'],
+            ['b', 'c']
+          ],
+          'exact match for JSON Pointer "/a" (JSON)'
         ],
-        'exact match for JSON Pointer "/a" (JSON)'
-      ],
-      ['same', [undefined, 'e'], 'exact match for JSON Pointer "/d" (JSON)'],
-      ['same', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (JSON)']
-    ]);
-  });
+        ['same', [undefined, 'e'], 'exact match for JSON Pointer "/d" (JSON)'],
+        ['same', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (JSON)']
+      ]);
+    });
 
-  await t.test('YAML', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    (await ua.getOk('/index.yaml'))
-      .yamlHas('/a')
-      .yamlHas('/d')
-      .yamlIs(['b', 'c'], '/a')
-      .yamlIs('e', '/d')
-      .yamlIs({a: ['b', 'c']});
-
-    t.same(results, [
-      ['ok', [true], 'GET request for /index.yaml'],
-      ['ok', [true], 'has value for JSON Pointer "/a" (YAML)'],
-      ['ok', [false], 'has value for JSON Pointer "/d" (YAML)'],
-      [
-        'same',
+    await t.test('YAML', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
+
+      (await ua.getOk('/index.yaml'))
+        .yamlHas('/a')
+        .yamlHas('/d')
+        .yamlIs(['b', 'c'], '/a')
+        .yamlIs('e', '/d')
+        .yamlIs({a: ['b', 'c']});
+
+      t.same(results, [
+        ['ok', [true], 'GET request for /index.yaml'],
+        ['ok', [true], 'has value for JSON Pointer "/a" (YAML)'],
+        ['ok', [false], 'has value for JSON Pointer "/d" (YAML)'],
         [
-          ['b', 'c'],
-          ['b', 'c']
+          'same',
+          [
+            ['b', 'c'],
+            ['b', 'c']
+          ],
+          'exact match for JSON Pointer "/a" (YAML)'
         ],
-        'exact match for JSON Pointer "/a" (YAML)'
-      ],
-      ['same', [undefined, 'e'], 'exact match for JSON Pointer "/d" (YAML)'],
-      ['same', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (YAML)']
-    ]);
-  });
+        ['same', [undefined, 'e'], 'exact match for JSON Pointer "/d" (YAML)'],
+        ['same', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (YAML)']
+      ]);
+    });
 
-  await t.test('WebSocket exception', async t => {
-    let result;
-    try {
-      await ua.messageOk();
-    } catch (error) {
-      result = error;
-    }
-    t.match(result, /No active WebSocket connection/);
-  });
+    await t.test('WebSocket exception', async t => {
+      let result;
+      try {
+        await ua.messageOk();
+      } catch (error) {
+        result = error;
+      }
+      t.match(result, /No active WebSocket connection/);
+    });
 
-  await t.test('WebSocket', async t => {
-    const results = [];
-    ua.assert = (name, args, msg) => results.push([name, args, msg]);
-
-    await ua.websocketOk('/echo');
-    await ua.sendOk('hello');
-    await ua.messageOk();
-    await ua.closeOk(1000);
-    await ua.closedOk(1000);
-
-    t.same(results, [
-      ['ok', [true], 'WebSocket handshake with /echo'],
-      ['ok', [true], 'send message'],
-      ['ok', [true], 'message received'],
-      ['ok', [true], 'closed WebSocket'],
-      ['equal', [1000, 1000], 'WebSocket closed with status 1000']
-    ]);
-  });
+    await t.test('WebSocket', async t => {
+      const results = [];
+      ua.assert = (name, args, msg) => results.push([name, args, msg]);
 
-  await t.test('Test without active connection', async t => {
-    const badAgent = await app.newTestUserAgent();
-
-    t.throws(
-      () => {
-        badAgent.statusIs(200);
-      },
-      {message: /No active HTTP response/}
-    );
-
-    let result;
-    try {
-      await badAgent.sendOk('fail');
-    } catch (error) {
-      result = error;
-    }
-    t.match(result, {message: /No active WebSocket connection/});
-
-    await badAgent.stop();
-  });
+      await ua.websocketOk('/echo');
+      await ua.sendOk('hello');
+      await ua.messageOk();
+      await ua.closeOk(1000);
+      await ua.closedOk(1000);
+
+      t.same(results, [
+        ['ok', [true], 'WebSocket handshake with /echo'],
+        ['ok', [true], 'send message'],
+        ['ok', [true], 'message received'],
+        ['ok', [true], 'closed WebSocket'],
+        ['equal', [1000, 1000], 'WebSocket closed with status 1000']
+      ]);
+    });
 
-  await t.test('Test redirects', async t => {
-    (await ua.postOk('/redirect')).statusIs(302).headerLike('location', /\?test=pass/);
+    await t.test('Test without active connection', async t => {
+      const badAgent = await app.newTestUserAgent();
+
+      t.throws(
+        () => {
+          badAgent.statusIs(200);
+        },
+        {message: /No active HTTP response/}
+      );
+
+      let result;
+      try {
+        await badAgent.sendOk('fail');
+      } catch (error) {
+        result = error;
+      }
+      t.match(result, {message: /No active WebSocket connection/});
+
+      await badAgent.stop();
+    });
 
-    const uaRedirect = await app.newTestUserAgent({tap: t, maxRedirects: 1});
-    (await uaRedirect.postOk('/redirect')).statusIs(200).bodyIs('Hi');
-    await uaRedirect.stop();
-  });
+    await t.test('Test redirects', async t => {
+      (await ua.postOk('/redirect')).statusIs(302).headerLike('location', /\?test=pass/);
 
-  await ua.stop();
+      const uaRedirect = await app.newTestUserAgent({tap: t, maxRedirects: 1});
+      (await uaRedirect.postOk('/redirect')).statusIs(200).bodyIs('Hi');
+      await uaRedirect.stop();
+    });
+    await ua.stop();
+  }
 });

From 2778372d78a4330b38d5fe99dad243cf4ba4dcb9 Mon Sep 17 00:00:00 2001
From: Daniel Mantovani <dmanto@gmail.com>
Date: Tue, 12 Nov 2024 17:24:06 -0300
Subject: [PATCH 2/2] refactor to add needed assert methods to TAP, reverting
 last commit's approach

---
 src/user-agent/test.ts  | 46 ++++++++++++++++++++++-------------------
 test/test-user-agent.js | 36 ++++++++++++++++----------------
 2 files changed, 43 insertions(+), 39 deletions(-)

diff --git a/src/user-agent/test.ts b/src/user-agent/test.ts
index 2091aea8..9ede0540 100644
--- a/src/user-agent/test.ts
+++ b/src/user-agent/test.ts
@@ -20,6 +20,16 @@ import StackUtils from 'stack-utils';
 
 type SkipFunction = (...args: any[]) => any;
 
+// Helper function to add required `TestUserAgent` assert methods
+// that are missing in a `TAP` instance. This is as an intended side effect
+// to ensure compatibility with `TestUserAgent` class below
+function addNodeAssertMethods(tapInstance: Test): void {
+  (tapInstance as any).strictEqual = tapInstance.equal.bind(tapInstance);
+  (tapInstance as any).notStrictEqual = tapInstance.not.bind(tapInstance);
+  (tapInstance as any).doesNotMatch = tapInstance.notMatch.bind(tapInstance);
+  (tapInstance as any).deepStrictEqual = tapInstance.same.bind(tapInstance);
+}
+
 /**
  * Test user-agent class.
  */
@@ -46,15 +56,7 @@ export class TestUserAgent extends MockUserAgent {
    * Delegate assertion to test framework currently in use.
    */
   assert(name: string, args: any[], msg: string, skip: SkipFunction): void {
-    const test: any = this._assert ?? {
-      equal: assert.strictEqual,
-      not: assert.notStrictEqual,
-      match: assert.match,
-      notMatch: assert.doesNotMatch,
-      ok: assert.ok,
-      notOk: (value: any, message: string) => assert.ok(!value, message),
-      same: assert.deepStrictEqual
-    };
+    const test: any = this._assert ?? assert;
     test[name](...args, msg, {stack: this._stack.captureString(10, skip).replaceAll('file://', '')});
   }
 
@@ -62,7 +64,7 @@ export class TestUserAgent extends MockUserAgent {
    * Check response content for exact match.
    */
   bodyIs(body: string): this {
-    this.assert('equal', [this.body.toString(), body], 'body is equal', this.bodyIs);
+    this.assert('strictEqual', [this.body.toString(), body], 'body is equal', this.bodyIs);
     return this;
   }
 
@@ -70,7 +72,7 @@ export class TestUserAgent extends MockUserAgent {
    * Opposite of `bodyIs`.
    */
   bodyIsnt(body: string): this {
-    this.assert('not', [this.body.toString(), body], 'body is not equal', this.bodyIsnt);
+    this.assert('notStrictEqual', [this.body.toString(), body], 'body is not equal', this.bodyIsnt);
     return this;
   }
 
@@ -86,7 +88,7 @@ export class TestUserAgent extends MockUserAgent {
    * Opposite of `bodyLike`.
    */
   bodyUnlike(regex: RegExp): this {
-    this.assert('notMatch', [this.body.toString(), regex], 'body is not similar', this.bodyUnlike);
+    this.assert('doesNotMatch', [this.body.toString(), regex], 'body is not similar', this.bodyUnlike);
     return this;
   }
 
@@ -105,7 +107,7 @@ export class TestUserAgent extends MockUserAgent {
   async closedOk(code: number): Promise<void> {
     await this._waitFinished();
     const finished = this._finished == null ? null : this._finished[0];
-    this.assert('equal', [finished, code], `WebSocket closed with status ${code}`, this.closedOk);
+    this.assert('strictEqual', [finished, code], `WebSocket closed with status ${code}`, this.closedOk);
   }
 
   /**
@@ -159,7 +161,7 @@ export class TestUserAgent extends MockUserAgent {
    * Opposite of `headerExists`.
    */
   headerExistsNot(name: string): this {
-    this.assert('notOk', [this.res.get(name) !== null], `no "${name}" header`, this.headerExistsNot);
+    this.assert('ok', [this.res.get(name) === null], `no "${name}" header`, this.headerExistsNot);
     return this;
   }
 
@@ -167,7 +169,7 @@ export class TestUserAgent extends MockUserAgent {
    * Check response header for exact match.
    */
   headerIs(name: string, value: string): this {
-    this.assert('equal', [this.res.get(name), value], `${name}: ${value}`, this.headerIs);
+    this.assert('strictEqual', [this.res.get(name), value], `${name}: ${value}`, this.headerIs);
     return this;
   }
 
@@ -175,7 +177,7 @@ export class TestUserAgent extends MockUserAgent {
    * Opposite of `headerIs`.
    */
   headerIsnt(name: string, value: string): this {
-    this.assert('not', [this.res.get(name), value], `not ${name}: ${value}`, this.headerIsnt);
+    this.assert('notStrictEqual', [this.res.get(name), value], `not ${name}: ${value}`, this.headerIsnt);
     return this;
   }
 
@@ -202,7 +204,7 @@ export class TestUserAgent extends MockUserAgent {
    */
   jsonIs(value: JSONValue, pointer = ''): this {
     const expected = jsonPointer(JSON.parse(this.body.toString()), pointer);
-    this.assert('same', [expected, value], `exact match for JSON Pointer "${pointer}" (JSON)`, this.jsonIs);
+    this.assert('deepStrictEqual', [expected, value], `exact match for JSON Pointer "${pointer}" (JSON)`, this.jsonIs);
     return this;
   }
 
@@ -277,7 +279,7 @@ export class TestUserAgent extends MockUserAgent {
    * Check response status for exact match.
    */
   statusIs(status: number): this {
-    this.assert('equal', [this.res.statusCode, status], `response status is ${status}`, this.statusIs);
+    this.assert('strictEqual', [this.res.statusCode, status], `response status is ${status}`, this.statusIs);
     return this;
   }
 
@@ -299,7 +301,7 @@ export class TestUserAgent extends MockUserAgent {
    */
   textUnlike(selector: string, regex: RegExp): this {
     this.assert(
-      'notMatch',
+      'doesNotMatch',
       [this._html.at(selector)?.text() ?? '', regex],
       `no similar match for selector "${selector}"`,
       this.textLike
@@ -311,7 +313,7 @@ export class TestUserAgent extends MockUserAgent {
    * Check response `Content-Type` header for exact match.
    */
   typeIs(value: string): this {
-    this.assert('equal', [this.res.type, value], `Content-Type: ${value}`, this.typeIs);
+    this.assert('strictEqual', [this.res.type, value], `Content-Type: ${value}`, this.typeIs);
     return this;
   }
 
@@ -361,7 +363,7 @@ export class TestUserAgent extends MockUserAgent {
    */
   yamlIs(value: JSONValue, pointer = ''): this {
     const expected = jsonPointer(yaml.load(this.body.toString()) as JSONValue, pointer);
-    this.assert('same', [expected, value], `exact match for JSON Pointer "${pointer}" (YAML)`, this.yamlIs);
+    this.assert('deepStrictEqual', [expected, value], `exact match for JSON Pointer "${pointer}" (YAML)`, this.yamlIs);
     return this;
   }
 
@@ -371,11 +373,13 @@ export class TestUserAgent extends MockUserAgent {
   }
 
   _prepareTap(tap: Test): void {
+    addNodeAssertMethods(tap);
     this._assert = tap;
     const subtests = [tap];
     const assert = this._assert;
 
     assert.beforeEach(async t => {
+      addNodeAssertMethods(t);
       subtests.push(t);
       this._assert = t;
     });
diff --git a/test/test-user-agent.js b/test/test-user-agent.js
index b338b00c..1c04f80e 100644
--- a/test/test-user-agent.js
+++ b/test/test-user-agent.js
@@ -28,7 +28,7 @@ t.test('TestUserAgent', async t => {
 
   app.get('/target', ctx => ctx.render({text: 'Hi'}));
 
-  for (const useTap of [true, false]) {
+  for (const useTap of [false, true]) {
     const ua = useTap ? await app.newTestUserAgent({tap: t}) : await app.newTestUserAgent();
 
     await t.test('Status Assertion test', async () => {
@@ -96,7 +96,7 @@ t.test('TestUserAgent', async t => {
 
       t.same(results, [
         ['ok', [true], 'GET request for /'],
-        ['equal', [200, 200], 'response status is 200']
+        ['strictEqual', [200, 200], 'response status is 200']
       ]);
     });
 
@@ -115,12 +115,12 @@ t.test('TestUserAgent', async t => {
 
       t.same(results, [
         ['ok', [true], 'GET request for /'],
-        ['equal', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
+        ['strictEqual', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
         ['match', ['text/plain; charset=utf-8', /plain/], 'Content-Type is similar'],
         ['ok', [true], 'header "Content-Type" exists'],
-        ['notOk', [false], 'no "Content-Disposition" header'],
-        ['equal', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
-        ['not', ['text/plain; charset=utf-8', 'text/plain'], 'not Content-Type: text/plain'],
+        ['ok', [true], 'no "Content-Disposition" header'],
+        ['strictEqual', ['text/plain; charset=utf-8', 'text/plain'], 'Content-Type: text/plain'],
+        ['notStrictEqual', ['text/plain; charset=utf-8', 'text/plain'], 'not Content-Type: text/plain'],
         ['match', ['text/plain; charset=utf-8', /plain/], 'Content-Type is similar']
       ]);
     });
@@ -137,10 +137,10 @@ t.test('TestUserAgent', async t => {
 
       t.same(results, [
         ['ok', [true], 'GET request for /'],
-        ['equal', ['Hello Mojo!', 'test'], 'body is equal'],
-        ['not', ['Hello Mojo!', 'test'], 'body is not equal'],
+        ['strictEqual', ['Hello Mojo!', 'test'], 'body is equal'],
+        ['notStrictEqual', ['Hello Mojo!', 'test'], 'body is not equal'],
         ['match', ['Hello Mojo!', /that/], 'body is similar'],
-        ['notMatch', ['Hello Mojo!', /whatever/], 'body is not similar']
+        ['doesNotMatch', ['Hello Mojo!', /whatever/], 'body is not similar']
       ]);
     });
 
@@ -166,8 +166,8 @@ t.test('TestUserAgent', async t => {
         ['ok', [false], 'no element for selector "div"'],
         ['match', ['Two', /test/], 'similar match for selector "div"'],
         ['match', ['', /123/], 'similar match for selector "nothing"'],
-        ['notMatch', ['Two', /test/], 'no similar match for selector "div"'],
-        ['notMatch', ['', /123/], 'no similar match for selector "nothing"']
+        ['doesNotMatch', ['Two', /test/], 'no similar match for selector "div"'],
+        ['doesNotMatch', ['', /123/], 'no similar match for selector "nothing"']
       ]);
 
       results = [];
@@ -196,15 +196,15 @@ t.test('TestUserAgent', async t => {
         ['ok', [true], 'has value for JSON Pointer "/a" (JSON)'],
         ['ok', [false], 'has value for JSON Pointer "/d" (JSON)'],
         [
-          'same',
+          'deepStrictEqual',
           [
             ['b', 'c'],
             ['b', 'c']
           ],
           'exact match for JSON Pointer "/a" (JSON)'
         ],
-        ['same', [undefined, 'e'], 'exact match for JSON Pointer "/d" (JSON)'],
-        ['same', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (JSON)']
+        ['deepStrictEqual', [undefined, 'e'], 'exact match for JSON Pointer "/d" (JSON)'],
+        ['deepStrictEqual', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (JSON)']
       ]);
     });
 
@@ -224,15 +224,15 @@ t.test('TestUserAgent', async t => {
         ['ok', [true], 'has value for JSON Pointer "/a" (YAML)'],
         ['ok', [false], 'has value for JSON Pointer "/d" (YAML)'],
         [
-          'same',
+          'deepStrictEqual',
           [
             ['b', 'c'],
             ['b', 'c']
           ],
           'exact match for JSON Pointer "/a" (YAML)'
         ],
-        ['same', [undefined, 'e'], 'exact match for JSON Pointer "/d" (YAML)'],
-        ['same', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (YAML)']
+        ['deepStrictEqual', [undefined, 'e'], 'exact match for JSON Pointer "/d" (YAML)'],
+        ['deepStrictEqual', [{a: ['b', 'c']}, {a: ['b', 'c']}], 'exact match for JSON Pointer "" (YAML)']
       ]);
     });
 
@@ -261,7 +261,7 @@ t.test('TestUserAgent', async t => {
         ['ok', [true], 'send message'],
         ['ok', [true], 'message received'],
         ['ok', [true], 'closed WebSocket'],
-        ['equal', [1000, 1000], 'WebSocket closed with status 1000']
+        ['strictEqual', [1000, 1000], 'WebSocket closed with status 1000']
       ]);
     });