Peter
Peter

Reputation: 4701

Request handler unable to render view with Vision plugin

I'm working on a HapiJS api and I've defined a plugin that registers the Vision plugin and configures a rendering engine (ejs). But when I try and respond to the request by rendering a view I get the error

AssertionError [ERR_ASSERTION]: Missing views manager

If I register the Vision plugin and its config somewhere outside the plugin the tests pass but my expectation is that I should be able to encapsulate this logic in a plugin.

// plugin
const ejs = require('ejs');
const Vision = require('vision');

module.exports = {
    name: 'views',
    version: '0.0.1',
    register: async (server, { path }) => {
        await server.register(Vision);
        server.views({
            engines: { ejs },
            path,
        });
    },
};

The handler code is

// api
server.route({
  path: '/korin/songs',
  method: 'GET',
  handler: async (request, h) => {
    try {
      const acceptType = getMediaType(request.headers.accept);
      const data = await server.methods.getTopTracks({
        getTopTracks,
        lastfmApi,
      });
      if (acceptType === 'text/html') {
        return h.view('index'); // <-- this errors
      }
      return data;
    } catch (error) {
      console.warn(error);
    }
  },
});

The error is generated by a failing test which is

suite('render content', () => {
    test.only(`given text/html page should respond with header and footer`, async () => {
        const { server } = await setup();
        const { payload } = await server.inject({
            method: 'GET',
            url: '/korin/songs',
            headers: {
                accept: 'text/html',
            },
        });

        expect(payload).to.contain(`<header>`);
        expect(payload).to.contain(`<footer>`);
    });
});
// test setup
const setup = async options => {
    const server = new Hapi.Server();

    // truncated for brevity
    await server.register({
        plugin: require('../../server/api'),
        options: {
            ...defaults,
            ...options,
        },
    });

    await server.register({
        plugin: require('../../server/views'),
        options: { path: path.join(__dirname, '../views/templates') },
    });

    return {
        server
    };
};

Is there something I'm missing? I've tried running a console.log and the code seems to be running in the right order but failing anyway.

Upvotes: 0

Views: 492

Answers (1)

Stock Overflaw
Stock Overflaw

Reputation: 3321

There is an old thread on GitHub about this. TL;DR: the reference to server passed to the plugin when registering is slightly not the same as the "root" server. Some difference about realms, apparently still an issue.

Indeed: in the plugin, server.getViewsManager() (decorated by vision) after registration of vision and server.views will show something, whereas the same call in your route (so, after plugin registration) will show null. So much for "references".

I just tried a similar structure to you, got the same error, and this thread pointed me to a workaround: when registering your views plugin, just pass along a reference to the "real" server in the options.

// plugin
const ejs = require('ejs');
const Vision = require('vision');

module.exports = {
    name: 'views',
    version: '0.0.1',
    register: async (server, { path, realServer }) => { // <= added option
        await realServer.register(Vision); // <= replaced server w/ realServer
        realServer.views({ // <= replaced server w/ realServer
            engines: { ejs },
            path,
        });
    },
};
// test setup
// ...
    const server = new Hapi.Server();
    // ...
    await server.register({
        plugin: require('../../server/views'),
        options: {
            path: path.join(__dirname, '../views/templates'),
            realServer: server // <= added option
        }
    });

And, obviously, have the same options everywhere you register this plugin.

Upvotes: 1

Related Questions