Repository: nock/nock
Pull request #2564 (2023-12-09 00:15:24)

feat: add `Scope#clone()` method
## New Feature: `Scope#clone()` Method

This pull request introduces a new method `Scope#clone()` to the `Scope`. This method allows creating a new `Scope` with the same `basePath` and `scope options`.

### Motivation

This is particularly useful when we cannot wait for all requests to complete, and we want to check if the requests have been fulfilled using the `isDone()` method.

### Usage

Here is a simple usage example:

```javascript
describe('Example', () => {
  const baseScope = nock('http://example.test')

  it('GET scope is done', async () => {
    const get = baseScope.clone().get('/').reply(200)
    await got('http://example.test/')

    expect(get.isDone()).to.be.true()
  })

  it('POST scope is done', async () => {
      const post = baseScope.clone().post('/').reply(200)
      await got.post('http://example.test/')

      expect(post.isDone()).to.be.true()
  })
})
```

gr2m: What is the advantage over just using `nock('http://example.test')` instead of `baseScope.clone()`?
rsaryev: The advantage of using baseScope.clone() is the ability to create a new scope with the same parameters without duplicating the parameters in each test.

For example:

```javascript
describe('Example', () => {
    const baseScope = nock('http://www.example.com', {
        reqheaders: {
            authorization: 'Basic Auth',
        },
    })

    it('GET scope is done', async () => {
        const get = baseScope.clone().get('/').reply(200)
        await got('http://example.test/')

        expect(get.isDone()).to.be.true()
    })

    it('POST scope is done', async () => {
        const post = baseScope.clone().post('/').reply(200)
        await got.post('http://example.test/')

        expect(post.isDone()).to.be.true()
    })
})
```

You can generally do without baseScope.clone() and make a function that will create a scope with the necessary parameters. But it seems to me that this is not very convenient and I would like to have an interface that helps to clone baseScope with the necessary parameters.

For example:
```javascript
describe('Example', () => {
    const baseScope = () => nock('http://www.example.com', {
        reqheaders: {
            authorization: 'Basic Auth',
        },
    })

    it('GET scope is done', async () => {
        const get = baseScope().get('/').reply(200)
        await got('http://example.test/')

        expect(get.isDone()).to.be.true()
    })

    it('POST scope is done', async () => {
        const post = baseScope().post('/').reply(200)
        await got.post('http://example.test/')

        expect(post.isDone()).to.be.true()
    })
})
```

Patch:
diff --git a/README.md b/README.md
index a68b3c5..a4e111d 100644
--- a/README.md
+++ b/README.md
@@ -1066,6 +1066,17 @@ if (!nock.isActive()) {
 }
 ```
 
+### .clone()
+
+You can clone a scope by calling `.clone()` on it:
+
+```js
+const scope = nock('http://example.test')
+
+const getScope = scope.get('/').reply(200)
+const postScope = scope.clone().post('/').reply(200)
+```
+
 ## Restoring
 
 You can restore the HTTP interceptor to the normal unmocked behaviour by calling:
diff --git a/lib/scope.js b/lib/scope.js
index f952901..eb51e80 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -286,6 +286,10 @@ class Scope extends EventEmitter {
     this.date = d || new Date()
     return this
   }
+
+  clone() {
+    return new Scope(this.basePath, this.scopeOptions)
+  }
 }
 
 function loadDefs(path) {


Test patch:
diff --git a/tests/got/test_scope.js b/tests/got/test_scope.js
index 51a3a7e..5070334 100644
--- a/tests/got/test_scope.js
+++ b/tests/got/test_scope.js
@@ -217,4 +217,27 @@ describe('filteringRequestBody()', () => {
       'Invalid arguments: filtering request body should be a function or a regular expression',
     )
   })
+
+  describe('`Scope#clone()`', async () => {
+    it('creates a new Scope with the same basePath and scope options', () => {
+      const scope = nock('http://example.test', { encodedQueryParams: true })
+      const clonedScope = scope.clone()
+
+      expect(clonedScope.basePath).to.equal(scope.basePath)
+      expect(clonedScope.scopeOptions).to.deep.equal(scope.scopeOptions)
+    })
+
+    it('creates a new Scope that works independently', async () => {
+      const scope = nock('http://example.test')
+
+      const getScope = scope.get('/').reply(200)
+      const postScope = scope.clone().post('/').reply(200)
+
+      await got('http://example.test/')
+      expect(getScope.isDone()).to.be.true()
+
+      await got.post('http://example.test/')
+      expect(postScope.isDone()).to.be.true()
+    })
+  })
 })