Repository: nock/nock
Pull request #2553 (2023-11-03 04:11:53)

feat: add `context.query()` to nock back
When TDDing using Nock Back you're kind of in a Chicken-and-Egg situation.  You want to be able to verify the external calls you need to make - but you don't know the exact nature of those external calls.   This lets you list the interceptors created in a given fixture to make sure all the ones that you expect are there.

By itself, this functions as a negative expectation - like I can verify that certain calls do NOT happen in the fixture.  When paired with `assertScopesFinished` this can be used to verify the complete set of calls in the fixture.  For instance I have a fairly complex HTTP interactor that synchronizes a local data store with a remote one.  I know how many calls this takes - and I know that if the scopes in the fixture complete then my interaction worked.

But if I need to re-record my interaction, I need to go over to the fixture and check it manually.  This lets me make assertions about what is _in_ the fixture - so that I can re-record them and verify that they still do what I expect.

Example Code Simplified and Anonymized to the Point Of Inaccuracy:

```typescript
it('#synchronize - synchronize with the external API', async (localState) => {
  const { nockDone, context } = await back('http-interaction.json')
  
  const syncronizer = new Synchronizer(localState); 
  
  sycnronizer.syncronize();
  
  nockDone();
  
  context.assertScopesFinished();
  
  expect(context.query()).toEqual(
    expect.arrayContaining([
       expect.objectContaining({
           method: "POST",
           path: "/create/thing"
       }),
       expect.objectContaining({
          method: "POST",
          path: "create/thing"
      })
    ])
  );
})

```

gr2m: Thank you!

Patch:
diff --git a/README.md b/README.md
index 9b627b7..a68b3c5 100644
--- a/README.md
+++ b/README.md
@@ -1497,6 +1497,45 @@ To set the mode call `nockBack.setMode(mode)` or run the tests with the `NOCK_BA
 
 - lockdown: use recorded nocks, disables all http calls even when not nocked, doesn't record
 
+### Verifying recorded fixtures
+
+Although you can certainly open the recorded JSON fixtures to manually verify requests recorded by nockBack - it's sometimes useful to put those expectations in your tests.
+
+The `context.query` function can be used to return all of the interceptors that were recored in a given fixture.
+
+By itself, this functions as a negative expectation - you can verify that certain calls do NOT happen in the fixture. Since `assertScopesFinished` can verify there are no _extra_ calls in a fixture - pairing the two methods allows you to verify the exact set of HTTP interactions recorded in the fixture. This is especially useful when re-recording for instance, a service that synchronizes via several HTTP calls to an external API.
+
+**NB**: The list of fixtures is only available when reading. It will only be populated for nocks that are played back from fixtures.
+
+#### Example
+
+```js
+it('#synchronize - synchronize with the external API', async localState => {
+  const { nockDone, context } = await back('http-interaction.json')
+
+  const syncronizer = new Synchronizer(localState)
+
+  sycnronizer.syncronize()
+
+  nockDone()
+
+  context.assertScopesFinished()
+
+  expect(context.query()).toEqual(
+    expect.arrayContaining([
+      expect.objectContaining({
+        method: 'POST',
+        path: '/create/thing',
+      }),
+      expect.objectContaining({
+        method: 'POST',
+        path: 'create/thing',
+      }),
+    ]),
+  )
+})
+```
+
 ## Common issues
 
 **"No match for response" when using got with error responses**
diff --git a/lib/back.js b/lib/back.js
index f5040da..4128c0d 100644
--- a/lib/back.js
+++ b/lib/back.js
@@ -240,6 +240,23 @@ function load(fixture, options) {
     assertScopesFinished: function () {
       assertScopes(this.scopes, fixture)
     },
+    query: function () {
+      const nested = this.scopes.map(scope =>
+        scope.interceptors.map(interceptor => ({
+          method: interceptor.method,
+          uri: interceptor.uri,
+          basePath: interceptor.basePath,
+          path: interceptor.path,
+          queries: interceptor.queries,
+          counter: interceptor.counter,
+          body: interceptor.body,
+          statusCode: interceptor.statusCode,
+          optional: interceptor.optional,
+        })),
+      )
+
+      return [].concat.apply([], nested)
+    },
   }
 
   if (fixture && fixtureExists(fixture)) {
diff --git a/types/index.d.ts b/types/index.d.ts
index 9097b1e..4a6cc26 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -267,10 +267,23 @@ declare namespace nock {
     }>
   }
 
+  interface InterceptorSurface {
+    method: string
+    uri: string
+    basePath: string
+    path: string
+    queries?: string
+    counter: number
+    body: string
+    statusCode: number
+    optional: boolean
+  }
+
   interface BackContext {
     isLoaded: boolean
     scopes: Scope[]
     assertScopesFinished(): void
+    query: InterceptorSurface[]
   }
 
   interface BackOptions {


Test patch:
diff --git a/tests/test_back.js b/tests/test_back.js
index f97c31a..da5ca10 100644
--- a/tests/test_back.js
+++ b/tests/test_back.js
@@ -147,6 +147,24 @@ describe('Nock Back', () => {
     })
   })
 
+  it('`query` returns all of the interceptors recorded to the cassette', done => {
+    nockBack('good_request.json').then(({ nockDone, context }) => {
+      const interceptor = context.query()
+      nockDone()
+      expect(interceptor.length).to.equal(1)
+      expect(interceptor[0].method).to.equal('GET')
+      expect(interceptor[0].uri).to.equal('/')
+      expect(interceptor[0].basePath).to.equal('http://www.example.test:80')
+      expect(interceptor[0].path).to.equal('/')
+      expect(interceptor[0].queries).to.be.null()
+      expect(interceptor[0].counter).to.equal(1)
+      expect(interceptor[0]).to.have.property('body')
+      expect(interceptor[0].statusCode).to.equal(200)
+      expect(interceptor[0].optional).to.equal(false)
+      done()
+    })
+  })
+
   describe('wild mode', () => {
     beforeEach(() => {
       nockBack.setMode('wild')