This repository has been archived by the owner on Dec 5, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(project-partial): add new feature
test(project-partial): add more unit test * Try using golang table testing approach * coverage 100% docs(project-partials): update comments close #25
- Loading branch information
Showing
5 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
'use strict'; | ||
|
||
const {getPartials, createHelper} = require('../index'); | ||
const {merge} = require('lodash'); | ||
|
||
const mockLogger = { | ||
debug: jest.fn(), | ||
info () {}, | ||
warn () {}, | ||
error: jest.fn() | ||
}; | ||
|
||
describe('project-partial.createHelper', () => { | ||
const defaultScenario = { | ||
source_dir: '/root', | ||
log: mockLogger, | ||
render: { | ||
isRenderable: () => true, | ||
getOutput: () => 'html', | ||
renderSync: jest.fn().mockReturnValue('foo') | ||
}, | ||
theme_config: { | ||
partials: { | ||
head_start: './foo.ejs' | ||
} | ||
}, | ||
area: 'head_start' | ||
}; | ||
|
||
const scenarios = [{ | ||
message: 'should render one partial for head_start area', | ||
expected: { | ||
helper_output: 'foo' | ||
} | ||
}, | ||
{ | ||
message: 'should render multiple partials for head_start area', | ||
theme_config: { | ||
partials: { | ||
head_start: ['./foo.ejs', './foo.ejs', './foo.ejs'] | ||
} | ||
}, | ||
expected: { | ||
helper_output: 'foo\nfoo\nfoo' | ||
} | ||
}, { | ||
message: 'should render empty string because error occured while rendering', | ||
render: { | ||
renderSync: () => { throw new Error('foo'); } | ||
}, | ||
expected: { | ||
error: true, | ||
helper_output: '' | ||
} | ||
},{ | ||
message: 'should render empty string because area doesn\'t exist', | ||
area: 'foo_bar', | ||
expected: { | ||
helper_output: '' | ||
} | ||
}]; | ||
|
||
scenarios.map((scenario) => { | ||
const s = merge({}, defaultScenario, scenario); | ||
s.log.error.mockClear(); | ||
const helper = createHelper(s); | ||
|
||
it(s.message, () => { | ||
expect(s.log.error).not.toHaveBeenCalled(); | ||
expect(typeof helper).toEqual('function'); | ||
expect(helper(s.area)).toEqual(s.expected.helper_output); | ||
expect(helper(s.area)).toEqual(s.expected.helper_output); | ||
|
||
if (s.expected.error) { | ||
expect(s.log.error).toHaveBeenCalled(); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
describe('project-partial.getPartials', () => { | ||
const defaultScenario = { | ||
source_dir: '/root', | ||
log: mockLogger, | ||
}; | ||
|
||
const scenarios = [{ | ||
render: { | ||
isRenderable: () => true, | ||
getOutput: () => 'html' | ||
}, | ||
theme_config: { | ||
partials: { | ||
head_start: './foo.ejs' | ||
} | ||
}, | ||
expected: { | ||
head_start: ['/root/foo.ejs'] | ||
} | ||
}, | ||
{ | ||
render: { | ||
isRenderable: () => true, | ||
getOutput: () => 'jpeg' | ||
}, | ||
theme_config: { | ||
partials: { | ||
head_start: './foo.ejs' | ||
} | ||
}, | ||
expected: { | ||
head_start: [] | ||
} | ||
}]; | ||
|
||
scenarios.map((scenario) => { | ||
const s = Object.assign({}, defaultScenario, scenario); | ||
const partials = getPartials(s); | ||
|
||
it('should return array for every area', () => { | ||
Object.keys(partials).forEach((area) => { | ||
const p = partials[area]; | ||
expect(Array.isArray(p)).toBe(true); | ||
}); | ||
}); | ||
|
||
it('should transform a value into an array with one element', () => { | ||
expect(partials.head_start.length).toEqual(s.expected.head_start.length); | ||
}); | ||
|
||
it('should resolve partials\' path relative to project source dir', () => { | ||
expect(partials.head_start).toEqual(s.expected.head_start); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
'use strict'; | ||
|
||
const path = require('path'); | ||
|
||
function createHelper ({theme_config, source_dir, render, log}) { | ||
|
||
let partials = null; | ||
|
||
function projectPartialHelper (area) { | ||
|
||
// Get and normalize "partials" configuration once and cache the result | ||
// | ||
// We do this operation inside the helper function because: | ||
// | ||
// - it means that `project_partial` template helper is really used in the layout | ||
// - the rendering process is involved (it will not be excuted if you run `hexo clean`, for example) | ||
|
||
if (!partials) { | ||
partials = getPartials({ | ||
theme_config, source_dir, render, log | ||
}); | ||
} | ||
|
||
if (!partials[area]) { return ''; } | ||
|
||
// in a hexo helper plugin function, | ||
// "this" represents the same context that hexo pass | ||
// when rendering theme's layout partials | ||
const locals = this; | ||
return partials[area] | ||
.map((p) => renderPartial(p, locals)) | ||
.filter(h => h) | ||
.join('\n'); | ||
} | ||
|
||
function renderPartial (partialPath, locals) { | ||
try { | ||
return render.renderSync({ path: partialPath }, locals); | ||
} catch (err) { | ||
log.error(`There was a problem while rendering partial "${partialPath}". skip rendering.`); | ||
log.error(`${err.message}`); | ||
log.debug(err); | ||
return ''; | ||
} | ||
} | ||
|
||
return projectPartialHelper; | ||
} | ||
|
||
function getPartials ({theme_config, source_dir, render, log}) { | ||
const partials = Object.assign({ | ||
head_start: [], | ||
head_end: [], | ||
footer_start: [], | ||
footer_end: [] | ||
}, theme_config.partials); | ||
|
||
const isValidPartial = (p) => { | ||
return typeof p === 'string'; | ||
}; | ||
|
||
const isRenderablePartial = (p) => { | ||
if (!render.isRenderable(p) || render.getOutput(p) !== 'html') { | ||
log.warn(`partial "${p}" cannot be rendered or the output is not html.`); | ||
return false; | ||
} | ||
return true; | ||
}; | ||
|
||
// "normalize" partials for each area: | ||
// - always use array type for each area | ||
// - exclude invalid partials or non renderable partials | ||
return Object.keys(partials).reduce((normalizedPartials, area) => { | ||
|
||
if (!Array.isArray(partials[area])) { | ||
partials[area] = [partials[area]]; | ||
} | ||
|
||
normalizedPartials[area] = partials[area] | ||
.filter(isValidPartial) | ||
.map((p) => path.resolve(source_dir, p)) | ||
.filter(isRenderablePartial); | ||
|
||
return normalizedPartials; | ||
|
||
}, {}); | ||
} | ||
|
||
module.exports = { getPartials, createHelper }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
'use strict'; | ||
|
||
const {createHelper} = require('../lib/nodejs/project-partial'); | ||
|
||
module.exports = ({hexo}) => { | ||
|
||
hexo.extend.helper.register('project_partial', createHelper({ | ||
theme_config: hexo.config.theme_config, | ||
source_dir: hexo.source_dir, | ||
render: hexo.render, | ||
log: hexo.log | ||
})); | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters