"use strict"

var assert = require("assert")
var express = require("../")
  , request = require("supertest");

describe("app", function(){
  describe(".param(fn)", function(){
    it("should map app.param(name, ...) logic", function(done){
      var app = express();

      app.param(function(name, regexp){
        if (Object.prototype.toString.call(regexp) === "[object RegExp]") { // See #1557
          return function(req, res, next, val){
            var captures;
            if (captures = regexp.exec(String(val))) {
              req.params[name] = captures[1];
              next();
            } else {
              next("route");
            }
          }
        }
      })

      app.param(":name", /^([a-zA-Z]+)$/);

      app.get("/user/:name", function(req, res){
        res.send(req.params.name);
      });

      request(app)
      .get("/user/tj")
      .expect(200, "tj", function (err) {
        if (err) return done(err)
        request(app)
        .get("/user/123")
        .expect(404, done);
      });

    })

    it("should fail if not given fn", function(){
      var app = express();
      assert.throws(app.param.bind(app, ":name", "bob"))
    })
  })

  describe(".param(names, fn)", function(){
    it("should map the array", function(done){
      var app = express();

      app.param(["id", "uid"], function(req, res, next, id){
        id = Number(id);
        if (isNaN(id)) return next("route");
        req.params.id = id;
        next();
      });

      app.get("/post/:id", function(req, res){
        var id = req.params.id;
        res.send((typeof id) + ":" + id)
      });

      app.get("/user/:uid", function(req, res){
        var id = req.params.id;
        res.send((typeof id) + ":" + id)
      });

      request(app)
        .get("/user/123")
        .expect(200, "number:123", function (err) {
          if (err) return done(err)
          request(app)
            .get("/post/123")
            .expect("number:123", done)
        })
    })
  })

  describe(".param(name, fn)", function(){
    it("should map logic for a single param", function(done){
      var app = express();

      app.param("id", function(req, res, next, id){
        id = Number(id);
        if (isNaN(id)) return next("route");
        req.params.id = id;
        next();
      });

      app.get("/user/:id", function(req, res){
        var id = req.params.id;
        res.send((typeof id) + ":" + id)
      });

      request(app)
        .get("/user/123")
        .expect(200, "number:123", done)
    })

    it("should only call once per request", function(done) {
      var app = express();
      var called = 0;
      var count = 0;

      app.param("user", function(req, res, next, user) {
        called++;
        req.user = user;
        next();
      });

      app.get("/foo/:user", function(req, res, next) {
        count++;
        next();
      });
      app.get("/foo/:user", function(req, res, next) {
        count++;
        next();
      });
      app.use(function(req, res) {
        res.end([count, called, req.user].join(" "));
      });

      request(app)
      .get("/foo/bob")
      .expect("2 1 bob", done);
    })

    it("should call when values differ", function(done) {
      var app = express();
      var called = 0;
      var count = 0;

      app.param("user", function(req, res, next, user) {
        called++;
        req.users = (req.users || []).concat(user);
        next();
      });

      app.get("/:user/bob", function(req, res, next) {
        count++;
        next();
      });
      app.get("/foo/:user", function(req, res, next) {
        count++;
        next();
      });
      app.use(function(req, res) {
        res.end([count, called, req.users.join(",")].join(" "));
      });

      request(app)
      .get("/foo/bob")
      .expect("2 2 foo,bob", done);
    })

    it("should support altering req.params across routes", function(done) {
      var app = express();

      app.param("user", function(req, res, next, user) {
        req.params.user = "loki";
        next();
      });

      app.get("/:user", function(req, res, next) {
        next("route");
      });
      app.get("/:user", function (req, res) {
        res.send(req.params.user);
      });

      request(app)
      .get("/bob")
      .expect("loki", done);
    })

    it("should not invoke without route handler", function(done) {
      var app = express();

      app.param("thing", function(req, res, next, thing) {
        req.thing = thing;
        next();
      });

      app.param("user", function(req, res, next, user) {
        next(new Error("invalid invocation"))
      });

      app.post("/:user", function (req, res) {
        res.send(req.params.user);
      });

      app.get("/:thing", function (req, res) {
        res.send(req.thing);
      });

      request(app)
      .get("/bob")
      .expect(200, "bob", done);
    })

    it("should work with encoded values", function(done){
      var app = express();

      app.param("name", function(req, res, next, name){
        req.params.name = name;
        next();
      });

      app.get("/user/:name", function(req, res){
        var name = req.params.name;
        res.send("" + name);
      });

      request(app)
      .get("/user/foo%25bar")
      .expect("foo%bar", done);
    })

    it("should catch thrown error", function(done){
      var app = express();

      app.param("id", function(req, res, next, id){
        throw new Error("err!");
      });

      app.get("/user/:id", function(req, res){
        var id = req.params.id;
        res.send("" + id);
      });

      request(app)
      .get("/user/123")
      .expect(500, done);
    })

    it("should catch thrown secondary error", function(done){
      var app = express();

      app.param("id", function(req, res, next, val){
        process.nextTick(next);
      });

      app.param("id", function(req, res, next, id){
        throw new Error("err!");
      });

      app.get("/user/:id", function(req, res){
        var id = req.params.id;
        res.send("" + id);
      });

      request(app)
      .get("/user/123")
      .expect(500, done);
    })

    it("should defer to next route", function(done){
      var app = express();

      app.param("id", function(req, res, next, id){
        next("route");
      });

      app.get("/user/:id", function(req, res){
        var id = req.params.id;
        res.send("" + id);
      });

      app.get("/:name/123", function(req, res){
        res.send("name");
      });

      request(app)
      .get("/user/123")
      .expect("name", done);
    })

    it("should defer all the param routes", function(done){
      var app = express();

      app.param("id", function(req, res, next, val){
        if (val === "new") return next("route");
        return next();
      });

      app.all("/user/:id", function(req, res){
        res.send("all.id");
      });

      app.get("/user/:id", function(req, res){
        res.send("get.id");
      });

      app.get("/user/new", function(req, res){
        res.send("get.new");
      });

      request(app)
      .get("/user/new")
      .expect("get.new", done);
    })

    it("should not call when values differ on error", function(done) {
      var app = express();
      var called = 0;
      var count = 0;

      app.param("user", function(req, res, next, user) {
        called++;
        if (user === "foo") throw new Error("err!");
        req.user = user;
        next();
      });

      app.get("/:user/bob", function(req, res, next) {
        count++;
        next();
      });
      app.get("/foo/:user", function(req, res, next) {
        count++;
        next();
      });

      app.use(function(err, req, res, next) {
        res.status(500);
        res.send([count, called, err.message].join(" "));
      });

      request(app)
      .get("/foo/bob")
      .expect(500, "0 1 err!", done)
    });

    it("should call when values differ when using "next"", function(done) {
      var app = express();
      var called = 0;
      var count = 0;

      app.param("user", function(req, res, next, user) {
        called++;
        if (user === "foo") return next("route");
        req.user = user;
        next();
      });

      app.get("/:user/bob", function(req, res, next) {
        count++;
        next();
      });
      app.get("/foo/:user", function(req, res, next) {
        count++;
        next();
      });
      app.use(function(req, res) {
        res.end([count, called, req.user].join(" "));
      });

      request(app)
      .get("/foo/bob")
      .expect("1 2 bob", done);
    })
  })
})

Related articles

Express req.acceptsEncodings

"use strict" var express = require("../") , request = require("supertest"); describe("req", function(){ describe(".acceptsEncodings", function () { it("should return encoding if accepted", function (done) { var app = express(); app

Express req.query

"use strict" var assert = require("assert") var express = require("../") , request = require("supertest"); describe("req", function(){ describe(".query", function(){ it("should default to {}", function(done){ var app = createApp();

Express multi router

var app = require("../../examples/multi-router") var request = require("supertest") describe("multi-router", function(){ describe("GET /",function(){ it("should respond with root handler", function(done){ request(app) .get("/") .

Express res.links

"use strict" var express = require(".."); var request = require("supertest"); describe("res", function(){ describe(".links(obj)", function(){ it("should set Link header field", function (done) { var app = express(); app.use(function

Express middleware.basic

"use strict" var assert = require("assert") var express = require("../"); var request = require("supertest"); describe("middleware", function(){ describe(".next()", function(){ it("should behave like connect", function(done){ var app = expr

Express res.set

"use strict" var express = require(".."); var request = require("supertest"); describe("res", function(){ describe(".set(field, value)", function(){ it("should set the response header field", function(done){ var app = express(); app.

Express req.acceptsEncoding

"use strict" var express = require("../") , request = require("supertest"); describe("req", function(){ describe(".acceptsEncoding", function(){ it("should return encoding if accepted", function (done) { var app = express(); app.ge

Express res.sendFile

"use strict" var after = require("after"); var asyncHooks = tryRequire("async_hooks") var Buffer = require("safe-buffer").Buffer var express = require("../") , request = require("supertest") , assert = require("assert"); var onFinished = require("on

Express web service

var request = require("supertest") , app = require("../../examples/web-service"); describe("web-service", function(){ describe("GET /api/users", function(){ describe("without an api key", function(){ it("should respond with 400 bad reques

Express tmpl

var fs = require("fs"); var variableRegExp = /\$([0-9a-zA-Z\.]+)/g; module.exports = function renderFile(fileName, options, callback) { function onReadFile(err, str) { if (err) { callback(err); return; } try { str = str