Git Product home page Git Product logo

chai-http's Introduction

Chai HTTP semantic-release NPM version

HTTP integration testing with Chai assertions.

Features

  • integration test request composition
  • test http apps or external services
  • assertions for common http tasks
  • chai expect and should interfaces

Installation

This is an addon plugin for the Chai Assertion Library. Install via npm.

npm install chai-http

Plugin

Use this plugin as you would all other Chai plugins.

import chaiModule from "chai";
import chaiHttp from "chai-http";

const chai = chaiModule.use(chaiHttp);

To use Chai HTTP on a web page, please use the latest v4 version for now.

Integration Testing

Chai HTTP provides an interface for live integration testing via superagent. To do this, you must first construct a request to an application or url.

Upon construction you are provided a chainable api that allows you to specify the http VERB request (get, post, etc) that you wish to invoke.

Application / Server

You may use a function (such as an express or connect app) or a node.js http(s) server as the foundation for your request. If the server is not running, chai-http will find a suitable port to listen on for a given test.

Note: This feature is only supported on Node.js, not in web browsers.

chai.request.execute(app)
  .get('/')

When passing an app to request; it will automatically open the server for incoming requests (by calling listen()) and, once a request has been made the server will automatically shut down (by calling .close()). If you want to keep the server open, perhaps if you're making multiple requests, you must call .keepOpen() after .request(), and manually close the server down:

const requester = chai.request.Request(app).keepOpen()

Promise.all([
  requester.get('/a'),
  requester.get('/b'),
])
.then(responses => { /* ... */ })
.then(() => requester.close())

URL

You may also use a base url as the foundation of your request.

chai.request.execute('http://localhost:8080')
  .get('/')

Setting up requests

Once a request is created with a given VERB (get, post, etc), you chain on these additional methods to create your request:

Method Purpose
.set(key, value) Set request headers
.send(data) Set request data (default type is JSON)
.type(dataType) Change the type of the data sent from the .send() method (xml, form, etc)
.attach(field, file, attachment) Attach a file
.auth(username, password) Add auth headers for Basic Authentication
.query(parmasObject) Chain on some GET parameters

Examples:

.set()

// Set a request header
chai.request.execute(app)
  .put('/user/me')
  .set('Content-Type', 'application/json')
  .send({ password: '123', confirmPassword: '123' })

.send()

// Send some JSON
chai.request.execute(app)
  .put('/user/me')
  .send({ password: '123', confirmPassword: '123' })

.type()

// Send some Form Data
chai.request.execute(app)
  .post('/user/me')
  .type('form')
  .send({
    '_method': 'put',
    'password': '123',
    'confirmPassword': '123'
  })

.attach()

// Attach a file
chai.request.execute(app)
  .post('/user/avatar')
  .attach('imageField', fs.readFileSync('avatar.png'), 'avatar.png')

.auth()

// Authenticate with Basic authentication
chai.request.execute(app)
  .get('/protected')
  .auth('user', 'pass')
  
// Authenticate with Bearer Token
chai.request.execute(app)
  .get('/protected')
  .auth(accessToken, { type: 'bearer' })  
  

.query()

// Chain some GET query parameters
chai.request.execute(app)
  .get('/search')
  .query({name: 'foo', limit: 10}) // /search?name=foo&limit=10

Dealing with the response - traditional

In the following examples we use Chai's Expect assertion library:

const { expect } = chai;

To make the request and assert on its response, the end method can be used:

chai.request.execute(app)
  .put('/user/me')
  .send({ password: '123', confirmPassword: '123' })
  .end((err, res) => {
     expect(err).to.be.null;
     expect(res).to.have.status(200);
  });
Caveat

Because the end function is passed a callback, assertions are run asynchronously. Therefore, a mechanism must be used to notify the testing framework that the callback has completed. Otherwise, the test will pass before the assertions are checked.

For example, in the Mocha test framework, this is accomplished using the done callback, which signal that the callback has completed, and the assertions can be verified:

it('fails, as expected', function(done) { // <= Pass in done callback
  chai.request.execute('http://localhost:8080')
  .get('/')
  .end((err, res) => {
    expect(res).to.have.status(123);
    done();                               // <= Call done to signal callback end
  });
});

it('succeeds silently!', () => {   // <= No done callback
  chai.request.execute('http://localhost:8080')
  .get('/')
  .end((err, res) => {
    expect(res).to.have.status(123);    // <= Test completes before this runs
  });
});

When done is passed in, Mocha will wait until the call to done(), or until the timeout expires. done also accepts an error parameter when signaling completion.

Dealing with the response - Promises

If Promise is available, request() becomes a Promise capable library - and chaining of thens becomes possible:

chai.request.execute(app)
  .put('/user/me')
  .send({ password: '123', confirmPassword: '123' })
  .then((res) => {
     expect(res).to.have.status(200);
  })
  .catch((err) => {
     throw err;
  });

Retaining cookies with each request

Sometimes you need to keep cookies from one request, and send them with the next (for example, when you want to login with the first request, then access an authenticated-only resource later). For this, .request.agent() is available:

// Log in
const agent = chai.request.agent(app)
agent
  .post('/session')
  .send({ username: 'me', password: '123' })
  .then((res) => {
    expect(res).to.have.cookie('sessionid');
    // The `agent` now has the sessionid cookie saved, and will send it
    // back to the server in the next request:
    return agent.get('/user/me')
      .then((res) => {
         expect(res).to.have.status(200);
      });
  });

Note: The server started by chai.request.agent(app) will not automatically close following the test(s). You should call agent.close() after your tests to ensure your program exits.

Assertions

The Chai HTTP module provides a number of assertions for the expect and should interfaces.

.status (code)

  • @param {Number} status number

Assert that a response has a supplied status.

expect(res).to.have.status(200);

.header (key[, value])

  • @param {String} header key (case insensitive)
  • @param {String|RegExp} header value (optional)

Assert that a Response or Request object has a header. If a value is provided, equality to value will be asserted. You may also pass a regular expression to check.

Note: When running in a web browser, the same-origin policy only allows Chai HTTP to read certain headers, which can cause assertions to fail.

expect(req).to.have.header('x-api-key');
expect(req).to.have.header('content-type', 'text/plain');
expect(req).to.have.header('content-type', /^text/);

.headers

Assert that a Response or Request object has headers.

Note: When running in a web browser, the same-origin policy only allows Chai HTTP to read certain headers, which can cause assertions to fail.

expect(req).to.have.headers;

.ip

Assert that a string represents valid ip address.

expect('127.0.0.1').to.be.an.ip;
expect('2001:0db8:85a3:0000:0000:8a2e:0370:7334').to.be.an.ip;

.json / .text / .html

Assert that a Response or Request object has a given content-type.

expect(req).to.be.json;
expect(req).to.be.html;
expect(req).to.be.text;

.charset

Assert that a Response or Request object has a given charset.

expect(req).to.have.charset('utf-8');

.redirect

Assert that a Response object has a redirect status code.

expect(res).to.redirect;
expect(res).to.not.redirect;

.redirectTo

  • @param {String|RegExp} location url

Assert that a Response object redirects to the supplied location.

expect(res).to.redirectTo('http://example.com');
expect(res).to.redirectTo(/^\/search\/results\?orderBy=desc$/);

.param

  • @param {String} parameter name
  • @param {String} parameter value

Assert that a Request object has a query string parameter with a given key, (optionally) equal to value

expect(req).to.have.param('orderby');
expect(req).to.have.param('orderby', 'date');
expect(req).to.not.have.param('limit');

.cookie

  • @param {String} parameter name
  • @param {String} parameter value

Assert that a Request or Response object has a cookie header with a given key, (optionally) equal to value

expect(req).to.have.cookie('session_id');
expect(req).to.have.cookie('session_id', '1234');
expect(req).to.not.have.cookie('PHPSESSID');
expect(res).to.have.cookie('session_id');
expect(res).to.have.cookie('session_id', '1234');
expect(res).to.not.have.cookie('PHPSESSID');

Releasing

chai-http is released with semantic-release using the plugins:

License

(The MIT License)

Copyright (c) Jake Luer [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

chai-http's People

Contributors

austince avatar barraponto avatar boly38 avatar cornelius-k avatar exotrom avatar gabemedrash avatar greenkeeperio-bot avatar hurrymaplelad avatar inukshuk avatar jamesmessinger avatar keithamus avatar leggsimon avatar logicalparadox avatar lucasfcosta avatar martypdx avatar mcky avatar nfreear avatar olrar avatar pezra avatar pgrm avatar richardpringle avatar sambostock avatar theodorewahle avatar thomashohn avatar tobitenno avatar trickfilm400 avatar vesln avatar vieiralucas avatar zackward avatar zewa666 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chai-http's Issues

Add `done` to documentation

TL;DR:

Documentation doesn't mention the required use of done:

// Fails, as expected.
it('should fail', function(done) {   // <= done parameter not mentioned in docs
  chai.request('https://github.com')
  .get('/')
  .end(function(err, res) {
    res.should.have.status(123) ;    // <= fails, as expected
    done() ;                         // <= done call not mentioned in docs
  }) ;
}) ;

// Silently passes!
it('silently passes', function() {   // <= missing done parameter
  chai.request('https://github.com')
  .get('/')
  .end(function(err, res) {
    res.should.have.status(123) ;    // <= passes silently (never fails)
                                     // <= missing call to done()
  }) ;
}) ;

Details

Nowhere in the documentation or examples do you mention the use of done when writing tests. In retrospect, it is obvious that it should be called in asynchronous test, but as a new user, not knowing this (and not seeing it in the documentation) was a huge pain. Tests using chai-http without done pass silently, which was difficult to debug not knowing where to look. The documentation should mention done at least once, and why it is required.

reading response's data / response data event

Is there a way to read response's data? Or am I missing something?

I have a proxy server functionality that i need to test and I would like to compare two json files. I tried res.on('data' but it seams that mocha finishes run before hand.

disable following redirects

I am testing a redirect with

return this.client.get('/')
    .then(res => {
        expect(res).to.redirect;
    });

Everything works however when I inspect res I see that it followed all the redirects (3 in all).
This is timing out the test sometimes. We need an option to disable following redirects.

HTTP POST with form fields not working

see http://stackoverflow.com/a/36938187/6113110

It seems that simply the chai-http documentation is wrong. It sais:

// Send some Form Data
chai.request(app)
 .post('/user/me')
 .field('_method', 'put')
 .field('password', '123')
 .field('confirmPassword', '123')

Which does NOT work. This worked for me:

chai.request(app)
  .post('/create')
  .send({ 
      title: 'Dummy title',
      description: 'Dummy description'
  })
  .end(function(err, res) { ... }

'npm run posttest' fails when COVERALLS_REPO_TOKEN is unset

NP-1B45EDC80F2C:chai-http farmisen$ unset COVERALLS_REPO_TOKEN
NP-1B45EDC80F2C:chai-http farmisen$ npm run posttest

> [email protected] posttest /Users/farmisen/dev/occamz/chai-http
> if [ -z $COVERALLS_REPO_TOKEN ]; then cat coverage/lcov.info | coveralls; fi


/Users/farmisen/dev/occamz/chai-http/node_modules/coveralls/bin/coveralls.js:18
        throw err;
        ^
Bad response: 422 {"message":"Couldn't find a repository matching this job.","error":true}

seems like the bash test for COVERALLS_REPO_TOKEN presence is backward.

release new version to npm?

Latest npm published version is 1.0.0, since then you've committed a lot of changes - maybe it's time to release those to npm?

Status codes different than 2xx shouldn't be trated as Promise rejections

Since Chai-Http is a testing utility, it shouldn't make the assumption that not 2xx responses are errors, because maybe (and probably) we're expecting status codes in the 4xx range (or in other ranges).

At this time, I have place a catch before the "then" call because the "then" method isn't triggered, and the last "catch" is triggered, making fail the test.

Example:

it('GET /agents/status with invalid appId returns 401 status and "Invalid app ID" message', function () {
        return serverPromise
            .then(function (server) {
                return chai.request(server).get('/agents/status?appId=');
            })
            .catch(function (err) {
                // not 2xx responses are treated as errors :( , it sucks.
                return err.hasOwnProperty('response') ? err.response : err;
            })
            .then(function (res) {
                res.should.have.status(401);
                res.body.should.have.property('error');
                res.body.error.should.have.property('message');
                res.body.error.message.should.equal('Invalid app ID');
            })
            .catch(registerError);
    });

without the first catch, which converts the error again to be a response, the last one is triggered... but this one is placed there to catch real errors, not the expected ones.

Can't use promise libraries

According to the documentation I should be able to use the following code to use a custom promise library for older node environments:

var chai = require('chai');
chai.use(require('chai-http'));

// Add promise support if this does not exist natively.
if (!global.Promise) {
  var q = require('q');
  chai.request.addPromises(q.Promise);
} 

But when I try it I get the following error: "Object has no method 'addPromises'".

redirects(0) causes then() does not work

if using redirects(5), .then() works well for a POST. But if using redirect(0), which I only want to get the first http req's response, .then() never run. The workaround is using .end(err, res) instead.

Can you help to fix this?

My original code:

agent.post('/login')
    .field("pwd", pwd)
    .field("user", uid)
    .then( function( res ) { 
        debug(res, 'POST login');
        expect(res).to.have.status(200);

    agent.get( res.body.location )   // get the location field from the json response body
        .redirects(0)
        .then( function( res ) { 
            console.log('==================');    // this is never printed out
        });
 });

JSON request with file upload?

Is it possible to upload json data and and attach a file at the same time?

The code I am looking at is:

it('Should upload a content file', (done) => {
    var data = {
        name: 'Test content',
        description: 'lorem ipsum'
    }
    chai.request(url)
        .post(`/api/content?token=${token}`)
        .attach('file', fs.readFileSync(filepath), filename)
        .send(data)
        .end(function(err, res) {
            console.log(res.body, res.err)
            if (err) { done(err); }
            res.should.have.status(200);
            res.should.be.json;
            //res.body.should.be.a('array');
            //res.body.length.should.be.equal(0);
            done();
        });
});

When I do this I only get the file. Commenting out the 'attach' line gets be the name and description, but then I don't have the file.

The server side is being handled by express and express-fileupload:

createContent(req, res, next) {
     var name = req.body.name;
     var description = req.body.description;

    if (!req.files || !req.files.file) {
        res.json({
            message: 'no file uploaded'
        });
        return;
    }

   winston.debug('name', name);
   winston.debug('description', description);
   winston.debug('filename', req.files.file.name);

}

Login using Passport not working occasionally

We use mocha, chai and chaiHttp to spin up a express server and make post and get calls.

When running the server and logging in inside of our test (on codeship) using ChaiHttp, We get req.user = undefined occasionally.

For the most of the logins we get successful deSerialization and all show that the users details are serialized into the session after logging in.

   "express": "4.10.7",
  "express-session": "^1.5",
   "passport": "^0.3.2",
    "passport-local": "^1.0.0",
    "passport-local-mongoose": "^4.0.0"

Express App is configured like so:

app.use(express.static(path.join(__dirname, '../client')));

    app.use(bodyParser ());

    app.use(bodyParser.urlencoded ({
        extended:true
    }));

    app.use(bodyParser.json ());


    app.use(morgan('dev'));


    //app.use(cookieParser('abs'));

    var mongoStore = new MongoStore ({
        mongooseConnection: mongoose.connection,
        db: db.connection
    });

    var sessionOpts = {
      saveUninitialized: true, // saved new sessions
      resave: false, // do not automatically write to the session store
      store: mongoStore,
      secret: 'abs',
      cookie : {secure:false, maxAge: 2419200000 } // configure when sessions expires
    }

    app.use(session(sessionOpts));

    app.use(flash());
    app.use(passport.initialize());
    app.use(passport.session());

Could this be an issue with express-session or with ChaiHttp? It seems to work properly when calling login/logout on the server directly.
Thanks.
-Deepak

Help with cookies

I'm using the following code to test my passport js api:

var agent = chai.request.agent(app);
agent
    .post("/accounts/signin")
    .send({ username: "admin", password: Config.admin_password })
    .then((res) => {
      let user = res.body;
      (user.username === "admin").should.be.ok;
      res.should.have.cookie("connect.sid");
      console.log(res);
      return agent.get("/accounts/me")
        .then((res2) => res2.should.have.status(200));
    })
    .catch((err) => {
      throw err;
    });

Doing it manually via the command line gives me:

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 170
Content-Type: application/json; charset=utf-8
Date: Sun, 18 Dec 2016 10:50:39 GMT
ETag: W/"aa-qQek/aYvw28iu282Y2x1Hw"
X-Powered-By: Express
set-cookie: connect.sid=s%3A5lQUj03Bf2DZwmMLJ7inb7PI5nzmMcTb.QXuh6xmxohZyG810dd5TrBcLFTDZrfuJ%2BsMo7oxEKYs; Domain=backend; Path=/; Expires=Sun, 18 Dec 2016 13:38:39 GMT; HttpOnly

{
    "__v": 0,
    "_id": "585669ff37c1ec29963be286",
    "password": "$2a$08$1TgBcXq66BtwqlndAxWqheRCr9J7/ilqXWhgqYv4a2.hSb9q4mmDm",
    "username": "admin"
}

The username === admin test passes but the cookie one does not... and when I comment out the cookie one, the the subsequent get request also fails (I think because of cookies not being maintained).

Can anyone tell me what I'm doing wrong?

Calling .then() multiple times on the same promise has weird side effects

I just spent an hour tracking down this problem... Calling .then() on a promise should cause a new promise to be returned. But chai-http modifies the original promise. This causes all sorts of problems when I actually try to use chai-http with a test runner which attaches its own handlers to a promises.

From chai-http/lib/request.js:

Test.prototype.then = function (onResolve, onReject) {
  if (!Test.Promise) {
    throw new Error('Tried to use chai-http with promises, but no Promise library set');
  }
  if (!this._promise) {
    var self = this;
    this._promise = new Test.Promise(function (resolve, reject) {
      self.end(function (err, res) {
        console.log('resolve on Request.end', !! err, !! res, onResolve, onReject)
        if (err) {
          reject(err);
        } else {
          resolve(res);
        }
      });
    });
  }
  this._promise = this._promise.then(onResolve, onReject);
  return this;
};

Sorry I don't have a unit test to demonstrate this problem, I just wanted to jot down this note quickly while it's fresh in my head, hopefully this will save someone else an hour of debugging...

Multiple parameter

Hi there,

I've been trying to send a request with a param list:

chai.request("myUrl").get("/v1/skus?id=123&id=456")...

this request got encoded and the server receives id[0]=123&id[1]=456 which is totally different than the expected format by the server.

Otherwise - using .query({id: "123", id: "456"}) or
.query({id: "123"})
.query({id: "456"})
override the initial value so only one param is getting sent.

Is there any reason the query param list get encoded ?
I might be doing it wrong - in this case could you please advice how to pass multiple param values ?

Cheers

How to properly test for HTTP failure responses

I am trying to test a condition wherein the API returns 500 (internal server error). I am doing the expect assertion in the catch block like this:

  it ('Creation of user without email address, should fail', function(done) {
      chai.request(app).post('/users')
         .send(sampleUsers.userWithoutEmail)
         .then(function(res) {
            return done(new Error('should have failed with 400'));
      }).catch(function(e){
        console.log("error:", e.status);
        expect(e).to.have.status(400);  // shd fail with 'bad request'
        return done();
      });
    });

The problem with this is, it gets stuck at the expect line and doesn't come out until test framework times out. I can see that the console.log line gets printed correctly without consuming time, but expect invocation doesn't finish.

What am I doing wrong here?
Is this the correct way to test errors?

Wish: testing method check for empty bodies

In some cases, a server will return result.body as a null value, in other cases it will return an empty response. Practically, the difference often doesn't matter. It would be nice if a method checked that either of these were met:

expect(res.body).to.be.null or expect(res.body).to.be.empty

Unable to Authenticate with Basic authentication

The following example doesn't work and doesn't appear to be tested.

// Authenticate with Basic authentication
chai.request(app)
  .auth('user', 'pass')
  .get('/protected')

The auth method doesn't even appear to exist:

> Object.keys(chai.request(app));
[ 'get',
  'post',
  'put',
  'head',
  'delete',
  'options',
  'trace',
  'copy',
  'lock',
  'mkcol',
  'move',
  'propfind',
  'proppatch',
  'unlock',
  'report',
  'mkactivity',
  'checkout',
  'merge',
  'm-search',
  'notify',
  'subscribe',
  'unsubscribe',
  'patch',
  'del' ]

No promise library set

Hello,

Just upgraded to the new chai-http and use the promises. I run my tests on Travis on node 0.10 and 0.11. Apparently 0.10 does not have native promises and i get this error Tried to use chai-http with promises, but no Promise library set.

Then i read the docs and now i understand this line:

If Promise is available, request() becomes a Promise capable library - and chaining of thens becomes possible:

How should i use this with node 0.10? Should i include https://github.com/kriskowal/q and set that on global.Promise?

Pass error message?

If I use Supertest it passes the error message in this scenario however Chai-HTTP does not; yet both are using Superagent 1.2. Any ideas?

  describe('GET /error/500', function () {
    it('should return a 500 error', function (done) {
      chai.request(app)
        .get('/error/500')
        .end(function (err, res) {
          expect(err).to.not.be.ok;
          // expect(err.message).to.equal('Internal Server Error');  <- Doesn't Work with Chai-HTTP
          expect(res).to.have.status(500);
          expect(res.type).to.equal('text/html');
          expect(res.text).to.contain('<span class="red">Testing 1, 2, 3!</span>');
          done();
        });
    });
  });

Where is the lib-cov/http

Hi,

I was doing automatization tests with karma, phantom on gitlab and my tests didn't pass.
So I've received the message saying:

Module not found: Error: Cannot resolve 'file' or 'directory' ./lib-cov/http in D:\DEV\GIT\Interno\HoursBankAPI\node_modules\chai-http
@ ./~/chai-http/index.js 1:72-97

so, firstly I'm sorry, I'm new with chai-http but I I haven't found anything in the documentation about lib-cov, could you explain me what is it and how is it works?

Add easy testing for XML and Javascript content-types, and UTF-8 charset

I love the existing support for JSON, HTML and text:

expect(req).to.be.html;

I propose extending this:

expect(req).to.be.xml;
expect(req).to.be.javascript;

The proposed solution makes some assumptions:

  1. The expected content-type for XML is application/xml
  2. The expected content-type for Javascript is text/javascript - I suggest this is still the norm on servers despite application/javascript being more correct.

I'm also interested in testing the character encoding:

expect(res).to.be.utf8;   // Check for "charset=UTF-8" - case sensitive :(

My use-case is @nfreear/ou-media-player-test ../test/ou-podcast-test.js

Thoughts?

Browser support

Chai and Superagent can both be run in Node or in web browsers. It would be great if your plugin worked in browsers too. This would allow people to write universal tests that can run in Node (great for CI) and in browsers (great for real-world testing). Mocha also supports both Node and web browsers, so it's dead-simple to accomplish truly universal tests.

I don't think it would even require many changes to the code, since most of your dependencies are already universal. And some dependencies (like cookie jar) wouldn't be needed when running in a browser. You could probably just bundle your existing code via Browserify or WebPack.

can't install chai-http

Hi, I'm trying to install chai-http in an ubuntu 16.04 machine, but once I execute
npm install chai-http --save-dev

I get the following error:

npm ERR! Linux 4.4.0-38-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "install" "chai-http" "--save-dev"
npm ERR! node v4.5.0
npm ERR! npm  v2.15.9
npm ERR! file /home/ubuntu/.npm/methods/1.1.2/package/package.json
npm ERR! code EJSONPARSE

npm ERR! Failed to parse json
npm ERR! Unexpected token 'T' at 1:1
npm ERR! TJ Holowaychuk","email":"[email protected]","url":"http://tjholowaychuk.com"},
npm ERR! ^
npm ERR! File: /home/ubuntu/.npm/methods/1.1.2/package/package.json
npm ERR! Failed to parse package.json data.
npm ERR! package.json must be actual JSON, not just JavaScript.
npm ERR!
npm ERR! This is not a bug in npm.
npm ERR! Tell the package author to fix their package.json file. JSON.parse

npm ERR! Please include the following file with any support request:
npm ERR!     /home/ubuntu/brewerly/api/npm-debug.log

Do I need some special package in order to install chai-http?
Thanks in advanced

Question: adding express-session user property to chai-http request?

Is there a way to add or manipulate properties of the req object in chai-http requests?

I want to test my API which uses express-session and authenticates any request based on the presence of the req.session.user object, as shown below:

app.use(function(req, res, next) {
  if (req.session.user) {
  console.log('Authenticated request\n');
  next();
  } else {
    console.log('Request not authenticated, request rejected\n');    
    res.status(403).json({'message': 'Request rejected'});;
  }
});

I'm writing tests for my API with chai-http as shown below:

describe('test', function(){
 it('/test', function(done){
   chai.request(server)
   .get('/test')
   .end(function(err, res){
     res.body.should.be.a('object');
   });
 });
});

Can I add the .session.user object to my chai-http requests so that they will pass the authentication?

Thanks!

cookie() doesnt assert on Agent cookies

Hi, I'm trying to use the cookie method and I'm getting the following error:

TypeError: Cannot call method 'split' of undefined at [object Object].<anonymous>
(http.js:307:48)

Here is my code:

var agent = chai.request.agent(app)

describe('POST /logout', function() {
  it('Should logout the user', function() {
    return agent.get('/api/v1/users/me')
                .then( function(res) {
                  expect(res).to.have.cookie('connect.sid')
                });
  });
});

I'm using v1.0.0

README incomplete

The README cuts off in the middle of a sentence and also doesn't seem to describe the plugin accurately.

Attach file gives 400 bad request

var file_path = '../data/invalid.csv';

chai.request(config.baseUrl)
.put(url)
.set('Accept', 'application/json')
.set('Content-Type', 'customHeader')
.attach('file', fs.readFileSync(file_path))
.end(function (err, res) {
res.should.have.status(500);
done();
});

I am expecting a 500 response (not here to argue, that's a good way to do things or not, it's a requirement, that I have to test) but I am getting 400

Testing the same manually (using POSTMAN) works just fine.

Any resolution will be highly appreciated. I am kinda blocked on this one..

does the request store the cookie?

Hi, I'm just wondering if the cookie is stored in some way if the header is set-cookie.

I'm writing some tests and after the login I want to do the rest on websockets where I would like to get the cookie on the upgradeReq, but I can't seem to find it.

Testing file download, res.files returns empty object

Originally asked here: http://stackoverflow.com/questions/40517088/using-mocha-chai-to-ensure-rest-api-serves-up-a-file/

I am wanting to download the response file in a test, but the req.files value is an empty object. Displays as {} when displayed with console. The superagent docs suggest that res.files should provide a file.

Note the endpoint works when testing with 'wget'.

The code I am using for my test

it('Should get a file', (done) => {
    chai.request(url)
        .get('/api/exercise/1?token=' + token)
        .end(function(err, res) {
            if (err) { done(err); }
            res.should.have.status(200);

            // Check the headers for type and size
            res.should.have.header('content-type');
            res.header['content-type'].should.be.equal('application/pdf');
            res.should.have.header('content-length');
            const size = fs.statSync(filepath).size.toString();
            res.header['content-length'].should.be.equal(size);

            // TODO get access to saved file and do tests on it
            //      tried 'res.files' but it returns {}                
        });
});

Is there something I am missing?

App errors are swallowed

I'm trying to test an error condition in my app. First I'm adding middleware that throws and expect this test to pass because there is no error handler in the app. However, it just hangs because the end handler and done are never called.

 it('throws', function(done) {
      var app = express();
      app.use(function(req, res, next) {
        throw new Error('Test failed request.');
      });

      chai.request(app)
        .get('/foo')
        .end(function(err, res) {
          expect(err).to.be.ok;
          done();
        });
});

Is this the right setup? Seems odd that things can just hang.

POST

This is a bit of a wall of text, but please bear with me:

Here is a POST attempt formatted using an entirely different toolchain for testing:

Time: Mon, 27 Jun 16 13:40:54 -0700
Source ip: 204.191.154.66

Headers (Some may be inserted by server)
REQUEST_URI = /post.php
QUERY_STRING = 
REQUEST_METHOD = POST
GATEWAY_INTERFACE = CGI/1.1
REMOTE_PORT = 19216
REMOTE_ADDR = 204.191.154.66
HTTP_CONNECTION = close
CONTENT_LENGTH = 64
HTTP_HOST = posttestserver.com
HTTP_TOKEN = text/plain
CONTENT_TYPE = application/x-www-form-urlencoded
UNIQUE_ID = V3GPVkBaMGUAAB1Uf04AAAAc
REQUEST_TIME_FLOAT = 1467060054.9575
REQUEST_TIME = 1467060054

Post Params:
key: 'grant_type' value: 'password'
key: 'username' value: '[email protected]'
key: 'password' value: 'password'
Empty post body.

Upload contains PUT data:
grant_type=password&username=hello%40world.com&password=password

And here is a POST attempt using Chai and Chai-HTTP:

Time: Mon, 27 Jun 16 13:47:29 -0700
Source ip: 204.191.154.66

Headers (Some may be inserted by server)
REQUEST_URI = /post.php
QUERY_STRING = 
REQUEST_METHOD = POST
GATEWAY_INTERFACE = CGI/1.1
REMOTE_PORT = 19275
REMOTE_ADDR = 204.191.154.66
HTTP_CONNECTION = close
CONTENT_LENGTH = 410
CONTENT_TYPE = multipart/form-data; boundary=--------------------------112369991749419303041318
HTTP_TOKEN = text/plain
HTTP_USER_AGENT = node-superagent/2.0.0
HTTP_ACCEPT_ENCODING = gzip, deflate
HTTP_HOST = posttestserver.com
UNIQUE_ID = V3GQ4UBaMGUAACUNdbQAAAAN
REQUEST_TIME_FLOAT = 1467060449.4647
REQUEST_TIME = 1467060449

Post Params:
key: 'grant_type' value: 'password'
key: 'username' value: '[email protected]'
key: 'password' value: 'password'
Empty post body.

== Multipart File upload. ==
Received 0 file(s)

Notice the difference in CONTENT_TYPE in particular (I think this is the source of my problem) where my previous tools would submit the POST using the 'application/x-www-form-urlencoded' format, Chai-HTTP seems to be using this 'multipart/form-data; boundary=--------------------------112369991749419303041318' format (which I understand about as well as I can resolve this issue on my own).

Am I somehow misusing Chai-HTTP's POST cababilities? Or does Chai-HTTP not allow for 'application/x-www-form-urlencoded' POST requests? I do not seem to be able to resolve this and it is the final hurdle for me to jump to make the transition to using a Mocha/Chai toolchain for my testing.

It's not working with response - Promises.

I have tried with q and es6-promise...in get method getting UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: res.send is not a function and i am able to console the response in express server just before res.json line.

Unable to test response status codes with 3.0.0 version

This fix renamed property in http response object from "statusCode" to "status": 1c5194f.

It broke my tests which run with restify client and node version 4.4.0. And even the documentation for latest node version (6.x) says that the name of the property in response object should be "statusCode" (https://nodejs.org/api/http.html#http_response_statuscode). I don't understand to that fix. How can we just use a different property name for getting status code from response object and expect that everything will work as before?

header regex not working

I'm having trouble with the regex functionality of the header expectations. It seems to fail no matter what.

expect(res).to.have.header('content-type', /json/);
and even
expect(res).to.have.header('content-type', /.*/);

Uncaught AssertionError: expected header 'content-type' to have value /.*/ but got 'application/api-problem+json; charset=utf-8'

Am I missing something?

expect cookie fails with a proper set-cookie header

In a test I'm making some expectations on the response. One of the expectations verifies the existence of a cookie in the form:

expect(res).to.have.cookie('cookieName');

The expectation fails while the response object contains the set-cookie header with the cookie.

Using v1.0.0

chai http post not working

http post to both internal and external service is failing .. with mocha, chai, chai-http

NOTE : successfully able to get the response with curl.
curl -H "Content-Type: application/json" -X POST -d '{"y":"heartbeat"}' http://localhost:3500/hb

http server code :

var server = http.createServer(function (req, res) {

 if(req.method=="POST" && req.url=="/hb"){
            var body = '';
            req.on('data', function (data) {
                body += data;
                console.log("Partial body: " + body);
            });
            req.on('end', function () {
                console.log("Body: " + body);

                var reqBody
                try {
                    reqBody= JSON.parse(body);
                } catch (e) {
                    console.log("Parse Exception : " + e);
                    reqBody=" Req Parse Exception ";
                }

                var r= {"a":1,"req":reqBody};
                res.setHeader('Content-type', 'application/json; charset=utf-8');
                res.statusCode = 200 ;
                res.end(JSON.stringify(r));
            });
        }

test code

var chai = require('chai');
var chaiHttp = require('chai-http');
var app = require('../lib/server');
var should = chai.should();
var expect = chai.expect();
chai.use(chaiHttp);


describe(' Server :', function() {
    /*describe('test heartbeat :', function () {
        it('check test:', function () {
            assert.equal(-1, [1,2,3].indexOf(5));
            assert.equal(-1, [1,2,3].indexOf(0));
        });
    });*/
    describe('Logic : ', function () {
        it('/hb  then: ', function (done) {
            chai.request('http://localhost:3500/')
                .post('hb')
                .send({"y":"heartbeat"})
                .then(function (res) {
                    //res.should.have.status(200);
                    expect(res).to.have.status(200);
                 //  expect(err).to.be.null;
                //    expect(res).to.have.status(200);
                //    console.log("CAlling DOne ........... ");
                    //done();
                }, function (err) {
                    console.log(err);
                    throw err;
                })/*.catch(function (err) {
                    throw err;
                    console.log("HB THEN ERR :");
                    console.log(err);
                });*/

        });
        it('/hb  end: ', function (done) {
            chai.request('http://localhost:3500/')
                .post('hb')
                .send({"y":"heartbeat"})
                .end(function (err, res) {
                    expect(res).to.have.status(200);
                    //res.should.have.status(200);
                    /*if(err){
                        console.log(err);
                    }else {
                        res.should.have.status(200);
                    }*/
                    done();
                }).catch(function (err) {
                    throw err;
                    console.log("HB END ERR :");
                    console.log(err);
                });
        });

        it('/cot GET : ', function (done) {
            chai.request("http://localhost:3500")
                .get('/cot')
                .end(function (err, res) {
                    res.should.have.status(200);
                    done();
                });
        });
    });

    describe('Web Requests : ', function () {
        it('/hb  : ', function (done) {
            chai.request(app)
                .post('POST /hb')
                .send({"n":"heartbeat"})
                .end(function (err, res) {
                    res.should.have.status(200);
                    //res.should.have.status(200);
                    done();
                });
               /* .then(function (res) {
                    expect(err).to.be.null;
                    expect(res).to.have.status(200);
                    console.log("CAlling DOne ........... ");
                    done();
                }, function (err) {
                    console.log(err);
                    throw err;
                });*/

            /*chai.request("http://localhost:3500")
             .post('/hb')
             .send({"n":"heartbeat"})
             .end(function (err, res) {
             expect(err).to.be.null;
             expect(res).to.have.status(200);
             //res.should.have.status(200);
             //done();
             });*/
        });
        it('/cot GET : ', function (done) {
            chai.request(app).get('/cot').then(function (res) {
                    expect(err).to.be.null;
                    expect(res).to.have.status(200);
                    console.log("CAlling DOne ........... ");
                   // done();
                }, function (err) {
                    console.log(err);
                    throw err;
                });
        });
    });

});

mocha output

 Logic : 
      1) /hb  then: 
double callback!
double callback!
double callback!
      2) /hb  end: 
      โœ“ /cot GET : 
    Web Requests : 
      3) /hb  : 
      4) /cot GET : 


  1 passing (2s)
  4 failing

  1)  Server : Logic :  /hb  then: :
     Uncaught Error: Max retries reached, transport in silent mode, OFFLINE
      at Socket.<anonymous> (node_modules/joplog/node_modules/winston-logstash/lib/winston-logstash.js:197:26)
      at TCP.close (net.js:485:12)

  2)  Server : Logic :  /hb  end: :
     Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.


  3)  Server : Web Requests :  /hb  : :
     TypeError: undefined is not a function
      at serverAddress (/usr/lib/node_modules/chai-http/lib/request.js:293:18)
      at new Test (/usr/lib/node_modules/chai-http/lib/request.js:214:53)
      at Object.obj.(anonymous function) [as post] (/usr/lib/node_modules/chai-http/lib/request.js:183:14)
      at Context.<anonymous> (test/test.js:94:18)

  4)  Server : Web Requests :  /cot GET : :
     TypeError: undefined is not a function
      at serverAddress (/usr/lib/node_modules/chai-http/lib/request.js:293:18)
      at new Test (/usr/lib/node_modules/chai-http/lib/request.js:214:53)
      at Object.obj.(anonymous function) [as get] (/usr/lib/node_modules/chai-http/lib/request.js:183:14)
      at Context.<anonymous> (test/test.js:122:31)

Issues sending certain mimeTypes with formdata

Maybe a superagent issue, but I have a test that attaches a csv/xls/xlsx (all in separate tests) to send to a busboy route. Busboy interprets the csv as a file, but the xls/xlsx as a field (form field). I have also seen the same behavior with png files, but works with jpg. Sending from the browser works fine.

// Works and interprets as file so I can pipe the data into a writeStream
chai.request(app)
  .post('/my-busboy-endpoint')
  .attach(fs.readFileSync('mycsvfile.csv'), 'mycsvfile.csv');

// Does not work and busboy interprets as a field
chai.request(app)
  .post('/my-busboy-endpoint')
  .attach(fs.readFileSync('myexcelfile.xls'), 'myexcelfile.xls');

redirectTo does not work for me

I use this as a route (express 4)

router.get('/', function(req, res) {
  res.redirect('/status');
});

When I expect(res).to.redirectTo('/status') the status code is 200 and not a 301, 302, or 303.
Express is actually working and I do get a redirect. Can it be that chai-http is following redirects and thus returns the final 200?

My old code works and looks like this:

expect(res.redirects).to.have.length(1);
expect(res.redirects[0]).to.contain('/status');

Related PR: #9
And maybe @hurrymaplelad can take a look?

Settings headers and delete requests

It seems like there's some sort of weird thing going on with the del method paired with the setting of any headers. Example:

chai.request(app)
  .del("/any_url")
  .set('Any-Header', 'xxx')
  .res(function(res){ console.log(res); })

What's strage about this is that the test passes fully, hitting the right method with the right headers, but it does not hit the console.log and fails with the following error message:

TypeError: Object #<Test> has no method 'set'

If you want me to take a look into this and can push me in the right direction, would be happy to take a stab at it! It's really messing up my test suite ๐Ÿ˜ข

EDIT: Same thing happens with get

Documentation on Promises is not clear enough OR a better Error message is required

It may seem obvious to others but I spent a while trying to figure out promises because they are not compatible with done

my original test looked like this:

it('it should be able to Create a new User', (done) => {
        chai.request(server)
            .post('/api/users')
            .send({email:'[email protected]', password:'abc123'})
            .then((err, res) => {
                console.log(res);
                res.should.have.status(200);
                res.text.should.be.eq('You must send the username and the password');
              done();
            })
            .catch(done);
      });

but I kept getting the error

Error: Bad Request
      at Test.Request.callback (node_modules/superagent/lib/node/index.js:626:17)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:795:18)
      at endReadableNT (_stream_readable.js:974:12)
      at _combinedTickCallback (internal/process/next_tick.js:74:11)
      at process._tickCallback (internal/process/next_tick.js:98:9)

When I removed the done, the test passed

it('it should be able to Create a new User', () => {
        chai.request(server)
            .post('/api/users')
            .send({email:'[email protected]', password:'abc123'})
            .then((err, res) => {
                console.log(res);
                res.should.have.status(200);
                res.text.should.be.eq('User Created');
            })
            .catch((err) => {
                throw err
          });
      });

How do I post with this thing?!

   chai.request(url_params)
        .post(get_params)
        .send(post_data)
        .res((response)->
          console.warn response
          res = response
          done()
      )

no luck

Some responses doesn't contain text

Test case: https://gist.github.com/vslinko/5244480
Execution output:

$ make
node test.js
chai-http text.txt
supertest text.js
chai-http text.js

/Users/vyacheslav/q/test/node_modules/chai/lib/chai/assertion.js:107
    throw new AssertionError({
          ^
expected undefined to match /Hooray/
make: *** [test] Error 1

Tested with original chai-http and my PR #3

Can't test expected error responses because superagent throws

Consider the following Mocha test (written in ES7 async/await style which is syntactic sugar for Promises):

describe('Querying a nonexistent table', function() {
  it('causes a 404', async function() {
    const res = await chai.request(app).get('/faketable');
    res.should.have.status(404);
  });
});

If the endpoint returns a 404, the response promise gets rejected with an Error originating in superagent's response handler, and the test fails before reaching the status assertion. Other 4xx/5xx status codes similarly cannot be tested for in this way. I think this is surprising behavior that makes testing error responses much harder than it should be.

Update to superagent 1.0

Hi, I wanted to know if there are any plans to update to Superagent 1.0. It has been released only few days ago, but I'm very interested in this fix: ladjs/superagent@8c3858a as I need to test if my API returns a null value or not.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.