7

I'm trying to create a extension on supertest.

Using what I found in question Extending SuperTest. I have this working example on javascript:

const request = require('supertest');
const Test = request.Test;

Test.prototype.authenticate = function(user) {
  const {token, xsrfToken} = user.tokens;

  return this
   .set('Authorization', `Bearer ${token}`)
   .set('X-XSRF-TOKEN', xsrfToken);
}

And inside a test block I can use:

request(app)
  .post('/user/settings')
  .authenticate(user)
  .send(...)

This works fine. The problem now is to use the extension in a *.test.ts file.

As suggested in Extend Express Request object using Typescript, I try to create a file to use the typescript feature Declaration Merging.

// file location: ./src/types/supertest

declare namespace supertest {
  export interface Test {
    authenticate(user: any): this; // I didn't put a type on user to simplify here.
  }
}

and also changed my tsconfig.json

{
  "compilerOptions": {

    ...

    "typeRoots": ["./src/types"],

    ...

  }
}

But when I run npx tsc

$ npx tsc
src/api/user.test.ts:51:8 - error TS2551: Property 'authenticate' does not exist on type 'Test'.

51       .authenticate(user);
          ~~~~~~~

Question
Is there a way to fix this on typescript environment?

[EDIT]
Extra Information (not necessarily useful):
In the same project I have extensions on express, chai and pdf-merge-js. All of them works fine using the approach described above.

There is something peculiar about supertest maybe about @types/supertest that is preventing it to work.

This is a little bit of the code in my project that already work's for express:

// file location: ./src/types/express
import { ModelBase } from '../../models/base';

declare global {
  namespace Express {
    export interface Response {
      model: (model: ModelBase) => this;
    }
  }
}
Lee Goddard
  • 10,680
  • 4
  • 46
  • 63
Jonny Piazzi
  • 3,684
  • 4
  • 34
  • 81
  • At this answer [Extend Express Request object using Typescript](https://stackoverflow.com/questions/37377731/extend-express-request-object-using-typescript/40762463#40762463) it says you have to add the **whole file path** to `tsconfig.json` **`files`** array, not `typeRoots`. Try to add the file to `files` inside `tsconfig`. – Christos Lytras Apr 03 '20 at 20:54
  • In my project I have extensions for express and chai all work perfectly, using this setup, only supertest didn't work. But I tested your theory anyway, didn't work, thanks for the help. – Jonny Piazzi Apr 04 '20 at 00:52
  • I updated the question with a little more information. – Jonny Piazzi Apr 04 '20 at 01:04
  • It's not my theory, it's what the answer you're referencing suggests and you're missing. – Christos Lytras Apr 04 '20 at 07:38
  • Can you try to add this file in your user.test.ts? – Gourav Garg Apr 05 '20 at 07:50
  • I'm not sure I understand what you mean about add. If is to put the interface inside the test file, the problem will be that if I declare somenthing inside of the file I cannot import something else with the same name. – Jonny Piazzi Apr 05 '20 at 19:35
  • I mean import './src/types/supertest'; not the interface just this and it should work – Gourav Garg Apr 06 '20 at 11:02

3 Answers3

7

I'm not sure why, but this worked for me:

declare module "supertest" {
  interface Test extends superagent.SuperAgentRequest {
    authenticate(user: any): this;
  }
}

Structure:

index.ts
tsconfig.json
types
  - supertest
    - index.d.ts

My index.ts:

import request from "supertest";
import express from "express";

const app = express();

app.get("/user", function (req, res) {
  res.status(200).json({ name: "john" });
});

request(app)
  .post("/user/settings")
  .authenticate({ tokens: { token: "", xsrfToken: "" } })
  .send();

My tsconfig.json:

{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": ".",
    "typeRoots": ["./types", "./node_modules/@types"],
    "esModuleInterop": true
  },
  "files": ["./types/supertest/index.d.ts", "index.ts"]
}

My types/supertest/index.d.ts:

import superagent from "superagent";

declare module "supertest" {
  interface Test extends superagent.SuperAgentRequest {
    authenticate(user: any): this;
  }
}

I think that declare module "supertest" is the key part. Everything else you got right.

Maxim Mazurok
  • 3,856
  • 2
  • 22
  • 37
7

This one worked for me well.

First I've imported types

npm i -D @types/supertest  

And then I've used

import { agent as request } from "supertest"; 
-3

I'd trough with this problem and i solve it just import on test file

import { agent as request } from "supertest";