Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,31 @@ app.render = function render(name, options, callback) {
tryRender(view, renderOptions, done);
};

/**
* Render the given view `name` with `options`, returning a Promise.
*
* Example:
*
* const html = await app.renderAsync('email', { name: 'Tobi' });
*
* @param {String} name
* @param {Object} options
* @return {Promise<String>}
* @public
*/

app.renderAsync = function renderAsync(name, options) {
var self = this;
var opts = options || {};

return new Promise(function(resolve, reject) {
self.render(name, opts, function(err, str) {
if (err) reject(err);
else resolve(str);
});
});
};

/**
* Listen for connections.
*
Expand Down
109 changes: 109 additions & 0 deletions test/app.render.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,115 @@ describe('app', function(){
})
})
})

describe('.renderAsync(name, options)', function(){
it('should return a promise', function(done){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))
app.locals.user = { name: 'tobi' };

var result = app.renderAsync('user.tmpl');
assert.ok(result instanceof Promise);
result.then(function(str) {
assert.strictEqual(str, '<p>tobi</p>')
done();
}).catch(done);
})

it('should resolve with rendered string', function(done){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))

app.renderAsync('email.tmpl')
.then(function(str) {
assert.strictEqual(str, '<p>This is an email</p>')
done();
})
.catch(done);
})

it('should work with async/await', async function(){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))
app.locals.user = { name: 'tobi' };

var str = await app.renderAsync('user.tmpl');
assert.strictEqual(str, '<p>tobi</p>')
})

it('should accept options', async function(){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))

var str = await app.renderAsync('user.tmpl', { user: { name: 'jane' } });
assert.strictEqual(str, '<p>jane</p>')
})

it('should reject when view does not exist', function(done){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))

app.renderAsync('nonexistent.tmpl')
.then(function() {
done(new Error('should have rejected'));
})
.catch(function(err) {
assert.ok(err);
assert.ok(err.message.includes('Failed to lookup view'));
done();
});
})

it('should reject on render error', function(done){
var app = express();

function View(name, options){
this.name = name;
this.path = 'fake';
}

View.prototype.render = function(options, fn){
throw new Error('render error!');
};

app.set('view', View);

app.renderAsync('something')
.then(function() {
done(new Error('should have rejected'));
})
.catch(function(err) {
assert.ok(err);
assert.strictEqual(err.message, 'render error!');
done();
});
})

it('should expose app.locals', async function(){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))
app.locals.user = { name: 'tobi' };

var str = await app.renderAsync('user.tmpl', {});
assert.strictEqual(str, '<p>tobi</p>')
})

it('should give precedence to renderAsync() locals', async function(){
var app = createApp();

app.set('views', path.join(__dirname, 'fixtures'))
app.locals.user = { name: 'tobi' };

var str = await app.renderAsync('user.tmpl', { user: { name: 'jane' } });
assert.strictEqual(str, '<p>jane</p>')
})
})
})

function createApp() {
Expand Down