2

i'm not sure why i am receiving this. I am trying to create a simple test while using @hapi/crumb. i am only registering it once in my server.js.

const Path = require("path");
const hapi = require("hapi");
const inert = require("inert");
const vision = require("vision");
const Ejs = require("ejs");
const Crumb = require("@hapi/crumb");
const Blankie = require("blankie");
const Scooter = require("@hapi/scooter");
const routes = require("./routes");

// Configure the server
const server = hapi.Server({
  host: "0.0.0.0",
  port: process.env.PORT || 3000,
  routes: {
    files: {
      relativeTo: Path.join(__dirname, "..", "public")
    },
    state: {
      parse: true,
      failAction: "ignore"
    },
    security: {
      xframe: true,
      noOpen: false
    },
    cors: {
      origin: ["banglarelief.org"],
      headers: ["Authorization"], // an array of strings - 'Access-Control-Allow-Headers'
      exposedHeaders: ["Accept"], // an array of exposed headers - 'Access-Control-Expose-Headers',
      additionalExposedHeaders: ["Accept"], // an array of additional exposed headers
      maxAge: 60,
      credentials: true // boolean - 'Access-Control-Allow-Credentials'
    }
  }
});

const plugins = async () => {
  const pluginsToRegister = [
    inert,
    vision,
    require("hapi-mobile-views"),
    { plugin: Crumb, options: { cookieOptions: { isSecure: false } } },
    Scooter,
    {
      plugin: Blankie,
      options: {} // specify options here
    }
  ];
  await server.register(pluginsToRegister);
};

const init = async () => {
  await plugins();
  server.state("player", {
    ttl: null,
    clearInvalid: true,
    isSecure: false
  });
  server.views({
    engines: { ejs: Ejs },
    path: `${__dirname}/views`,
    layout: "layout"
  });
  await server.route(routes);
  return server;
};


const start = async () => {
  try {
    await init();
    await server.start();
  } catch (err) {
    console.log(err);
    process.exit(1);
  }
};

module.exports = { init, start };

My test file is very basic and i have tried to move around where the start should be called but it keep throwing same error.

'use strict';

const Lab = require('@hapi/lab');
const { expect } = require('@hapi/code');
const { afterEach, beforeEach, describe, it } = exports.lab = Lab.script();
const { init, start } = require('../src/server');

let server = start();

describe('GET /', () => {
    //let server;
    //server = start();

    beforeEach(async () => {
        //server = start();
    });

    afterEach(async () => {
        //await server.stop();
    });

    it('responds with 200', async () => {
        const res = await server.inject({
            method: 'get',
            url: '/'
        });
        expect(res.statusCode).to.equal(200);
    });
});

I have been following https://hapijs.com/tutorials/testing?lang=en_US

shorif2000
  • 2,582
  • 12
  • 65
  • 137
  • with this line, `let server = start();` something weird may be happening since there is no await. Also, beforeEach and afterEach expect a promise to resolve, so I would test putting `server = await start();` inside of a `before` and then remove the beforeEach and afterEach – Katherine R Jun 03 '19 at 17:29
  • Yeah the issue appears to be related to `await init();` in `start`. You're trying to re-initialize the server before each test. Therefore, you're getting an assertion error about how the plugin was already registered. In the toy example in the docs, they don't have any plugins so it works for them. The solution is to only initialize the server's plugins once. However, I don't have a solution as I'm trying to figure out how to initialize it only once myself right now. – technogeek1995 Jul 17 '19 at 17:24
  • Related: https://stackoverflow.com/q/57095818/1913185 – technogeek1995 Jul 18 '19 at 13:52

1 Answers1

0

The solution seems to work if you break up your plugins function into two parts. One part will init 3rd party plugins like @Hapi/*. The other function will init your 1st party plugins that you wrote. You will only init the 3rd party plugins in your start function.

It's critical that you include { once: true } because that will prevent your error. It will only initialize the plugin once, which will prevent your error. You cannot always specify { once: true } on 3rd party plugins. Thus, we have to handle that a different way. Since we moved all the 3rd party plugins to their own function, which is invoked on start, that should prevent 3rd party plugins from causing an issue of being reinitialized.

const hapiPlugins = async () => {
  const pluginsToRegister = [
    inert,
    vision,
    require("hapi-mobile-views"),
    { plugin: Crumb, options: { cookieOptions: { isSecure: false } } },
    Scooter,
    {
      plugin: Blankie,
      options: {} // specify options here
    }
  ];
};

const myPlugins = async () => {
  await server.register([
    allOfMyPlugins...
  ],
  {
    once: true  //critical so that you don't re-init your plugins
  });
};

const init = async () => {
  server.state("player", {
    ttl: null,
    clearInvalid: true,
    isSecure: false
  });
  server.views({
    engines: { ejs: Ejs },
    path: `${__dirname}/views`,
    layout: "layout"
  });
  await server.route(routes);
  return server;
};


const start = async () => {
  try {
    await hapiPlugins();
    await init();
    await server.start();
  } catch (err) {
    console.log(err);
    process.exit(1);
  }
};

Then, you should be able to call init in your test's before function. Use that server object to inject.

technogeek1995
  • 3,185
  • 2
  • 31
  • 52