Repository: nock/nock
Pull request #2350 (2022-05-12 17:16:39)

Push persisted interceptors to the end instead of ignore on remove
Currently, if you do:
```
nock(..).get('/').reply(200).persist();
nock(..).get('/').reply(400); // this will never get called
```
The second interceptor won't get called ever.

But sometimes in tests (queue for examples), I want to do:
```
beforeAll(() => {
  // some default behaviour
  nock(..).get('/').reply(200).persist();
});
test('should ...', () => {
  // test specific scenario for this same endpoint above
  nock(..).get('/').reply(400)
})
```

I find a workaround using the body matcher function:
```
const createInterceptor = () => {
  nock(..).get('/', () => { 
    createInterceptor(); 
    return true; 
  })
  .reply(200)
}
```
Which pushes the interceptor to the end of the queue (`interceptors` array) and mimics the wanted behavior but is slower and way less elegant.

P.S: I'll add tests and documentation if we agree on the wanted behavior.

mikicho: Trying my luck.
@gr2m can you please take a look on this?
gr2m: > Currently, if you do:
> 
> ```
> nock(..).get('/').reply(200).persist();
> nock(..).get('/').reply(400); // this will never get called
> ```
> 
> The second interceptor won't get called ever.

Just to check, swapping the lines around works, but you can't do it because you call the `.perst()` line in a `beforeEach` block?

As long as no other tests break and you update docs, I'd be happy to help get it landed.

Sorry for the late response
mikicho: > Just to check, swapping the lines around works, but you can't do it because you call the `.perst()` line in a `beforeEach` block?

`beforeAll`, yes.

> As long as no other tests break and you update docs, I'd be happy to help get it landed.

Cool! I'll add relevant tests.
mikicho: @gr2m PTAL

Patch:
diff --git a/README.md b/README.md
index 3cddf54..b1a0cd3 100644
--- a/README.md
+++ b/README.md
@@ -1002,7 +1002,7 @@ const scope = nock('http://example.com')
   .reply(200, 'Persisting all the way')
 ```
 
-Note that while a persisted scope will always intercept the requests, it is considered "done" after the first interception.
+Note that while a persisted scope will always intercept the requests, it is considered "done" after the first interception, and they are pushed to the bottom of the stack after consumption.
 
 If you want to stop persisting an individual persisted mock you can call `persist(false)`:
 
diff --git a/lib/intercept.js b/lib/intercept.js
index a95eb51..1915609 100644
--- a/lib/intercept.js
+++ b/lib/intercept.js
@@ -110,7 +110,7 @@ function addInterceptor(key, interceptor, scope, scopeOptions, host) {
 }
 
 function remove(interceptor) {
-  if (interceptor.__nock_scope.shouldPersist() || --interceptor.counter > 0) {
+  if (--interceptor.counter > 0) {
     return
   }
 
@@ -122,6 +122,12 @@ function remove(interceptor) {
   // matching instance. I'm also not sure why we couldn't delete _all_
   // matching instances.
   interceptors.some(function (thisInterceptor, i) {
+    if (interceptor.__nock_scope.shouldPersist()) {
+      return thisInterceptor === interceptor
+        ? interceptors.push(interceptors.splice(i, 1)[0])
+        : false
+    }
+
     return thisInterceptor === interceptor ? interceptors.splice(i, 1) : false
   })
 }


Test patch:
diff --git a/tests/test_persist_optionally.js b/tests/test_persist_optionally.js
index 6fe06d9..1da7d6d 100644
--- a/tests/test_persist_optionally.js
+++ b/tests/test_persist_optionally.js
@@ -144,6 +144,25 @@ describe('`persist()`', () => {
     expect(scope.isDone()).to.be.true()
   })
 
+  it('persisted mocks go to the bottom of the stack after matching', async () => {
+    const scope = nock('http://example.test')
+      .persist()
+      .get('/')
+      .reply(200, 'Persisting all the way')
+
+    const specificTestScope = nock('http://example.test')
+      .get('/')
+      .reply(201, 'return different response once')
+
+    await got('http://example.test/')
+
+    expect(scope.isDone()).to.be.true()
+
+    await got('http://example.test/')
+
+    expect(specificTestScope.isDone()).to.be.true()
+  })
+
   it('persisted mocks appear in `pendingMocks()`', async () => {
     const scope = nock('http://example.test')
       .get('/abc')