Unit testing using Mocha and Chai (#59)

This commit is contained in:
Mimi 2020-07-29 10:41:54 +08:00 committed by GitHub
parent e02e104ac2
commit cfb4130825
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 679 additions and 148 deletions

1
.gitattributes vendored
View File

@ -1 +1,2 @@
source/lib/* linguist-vendored
test/* linguist-vendored

View File

@ -10,4 +10,5 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v1
- run: npm install
- run: npm run eslint
- run: npm test

View File

@ -1,4 +1,5 @@
.github/
test/
.editorconfig
.eslintrc.json
.gitattributes

View File

@ -1,45 +0,0 @@
const fs = require('fs');
const path = require('path');
const gulp = require('gulp');
const shell = require('gulp-shell');
const yaml = require('js-yaml');
gulp.task('lint', shell.task([
'npm run eslint'
]));
gulp.task('lint:stylus', shell.task([
'npm run stylint'
]));
gulp.task('validate:config', cb => {
const themeConfig = fs.readFileSync(path.join(__dirname, '_config.yml'));
try {
yaml.safeLoad(themeConfig);
return cb();
} catch (error) {
return cb(new Error(error));
}
});
gulp.task('validate:languages', cb => {
const languagesPath = path.join(__dirname, 'languages');
const errors = [];
fs.readdirSync(languagesPath).forEach(lang => {
if (!lang.endsWith('.yml')) return;
const languagePath = path.join(languagesPath, lang);
try {
yaml.safeLoad(fs.readFileSync(languagePath), {
filename: path.relative(__dirname, languagePath)
});
} catch (error) {
errors.push(error);
}
});
return errors.length === 0 ? cb() : cb(errors);
});
gulp.task('default', gulp.series('lint', 'validate:config', 'validate:languages'));

View File

@ -2,11 +2,11 @@
"name": "hexo-theme-next",
"version": "8.0.0-rc.4",
"description": "Elegant and powerful theme for Hexo.",
"main": "gulpfile.js",
"main": "package.json",
"scripts": {
"test": "gulp",
"eslint": "eslint source/js scripts/",
"stylint": "stylint source/css/"
"eslint": "eslint scripts/ source/js test/",
"stylint": "stylint source/css/",
"test": "mocha test/index.js"
},
"repository": {
"type": "git",
@ -24,17 +24,19 @@
},
"homepage": "https://theme-next.js.org",
"devDependencies": {
"chai": "4.2.0",
"eslint": "7.3.1",
"eslint-config-theme-next": "1.2.0",
"gulp": "4.0.2",
"gulp-shell": "0.8.0",
"hexo": "5.0.0",
"hexo-renderer-marked": "3.0.0",
"husky": "4.2.5",
"js-yaml": "3.14.0",
"mocha": "8.0.1",
"stylint": "2.0.0"
},
"husky": {
"hooks": {
"pre-commit": "gulp"
"pre-commit": "npm run eslint && npm test"
}
}
}

View File

@ -3,6 +3,11 @@
'use strict';
const crypto = require('crypto');
const nextFont = require('./font');
const nextUrl = require('./next-url');
hexo.extend.helper.register('next_font', nextFont);
hexo.extend.helper.register('next_url', nextUrl);
hexo.extend.helper.register('next_inject', function(point) {
return this.theme.injects[point]

View File

@ -1,9 +1,7 @@
/* global hexo */
'use strict';
// https://developers.google.com/fonts/docs/getting_started
hexo.extend.helper.register('next_font', function() {
module.exports = function() {
const config = this.theme.font;
if (!config || !config.enable) return '';
@ -24,4 +22,4 @@ hexo.extend.helper.register('next_font', function() {
// Merge extra parameters to the final processed font string
return fontFamilies ? `<link rel="stylesheet" href="${fontHost}/css?family=${fontFamilies}&display=swap&subset=latin,latin-ext">` : '';
});
};

View File

@ -1,16 +1,13 @@
/* global hexo */
'use strict';
const { htmlTag } = require('hexo-util');
const { parse } = require('url');
hexo.extend.helper.register('next_url', function(path, text, options = {}) {
const { config } = this;
module.exports = function(path, text, options = {}) {
const { config, theme } = this;
const data = parse(path);
const siteHost = parse(config.url).hostname || config.url;
const theme = hexo.theme.config;
let exturl = '';
let tag = 'a';
let attrs = { href: this.url_for(path) };
@ -58,4 +55,4 @@ hexo.extend.helper.register('next_url', function(path, text, options = {}) {
}
return htmlTag(tag, attrs, decodeURI(text), false);
});
};

View File

@ -2,11 +2,9 @@
* button.js | https://theme-next.js.org/docs/tag-plugins/button
*/
/* global hexo */
'use strict';
function postButton(args) {
module.exports = ctx => function(args) {
args = args.join(' ').split(',');
const url = args[0];
const text = (args[1] || '').trim();
@ -14,7 +12,7 @@ function postButton(args) {
const title = (args[3] || '').trim();
if (!url) {
hexo.log.warn('URL can NOT be empty.');
ctx.log.warn('URL can NOT be empty.');
}
if (icon.length > 0) {
if (!icon.startsWith('fa')) icon = 'fa fa-' + icon;
@ -22,7 +20,4 @@ function postButton(args) {
}
return `<a class="btn" href="${url}"${title.length > 0 ? ` title="${title}"` : ''}>${icon}${text}</a>`;
}
hexo.extend.tag.register('button', postButton, {ends: false});
hexo.extend.tag.register('btn', postButton, {ends: false});
};

View File

@ -2,22 +2,17 @@
* caniuse.js | https://theme-next.js.org/docs/tag-plugins/caniuse
*/
/* global hexo */
'use strict';
function caniUse(args) {
module.exports = ctx => function(args) {
args = args.join('').split('@');
const feature = args[0];
const periods = args[1] || 'current';
if (!feature) {
hexo.log.warn('Caniuse feature can NOT be empty.');
ctx.log.warn('Caniuse feature can NOT be empty.');
return '';
}
return `<iframe data-feature="${feature}" src="https://caniuse.bitsofco.de/embed/index.html?feat=${feature}&periods=${periods}&accessible-colours=false" frameborder="0" width="100%" height="400px"></iframe>`;
}
hexo.extend.tag.register('caniuse', caniUse);
hexo.extend.tag.register('can', caniUse);
};

View File

@ -2,15 +2,10 @@
* center-quote.js | https://theme-next.js.org/docs/tag-plugins/
*/
/* global hexo */
'use strict';
function centerQuote(args, content) {
module.exports = ctx => function(args, content) {
return `<blockquote class="blockquote-center">
${hexo.render.renderSync({ text: content, engine: 'markdown' })}
${ctx.render.renderSync({ text: content, engine: 'markdown' })}
</blockquote>`;
}
hexo.extend.tag.register('centerquote', centerQuote, {ends: true});
hexo.extend.tag.register('cq', centerQuote, {ends: true});
};

View File

@ -2,8 +2,6 @@
* group-pictures.js | https://theme-next.js.org/docs/tag-plugins/group-pictures
*/
/* global hexo */
'use strict';
const LAYOUTS = {
@ -123,17 +121,14 @@ const templates = {
}
};
function groupPicture(args, content) {
module.exports = ctx => function(args, content) {
args = args[0].split('-');
const group = parseInt(args[0], 10);
const layout = parseInt(args[1], 10);
content = hexo.render.renderSync({text: content, engine: 'markdown'});
content = ctx.render.renderSync({ text: content, engine: 'markdown' });
const pictures = content.match(/<img[\s\S]*?>/g);
return `<div class="group-picture">${templates.dispatch(pictures, group, layout)}</div>`;
}
hexo.extend.tag.register('grouppicture', groupPicture, {ends: true});
hexo.extend.tag.register('gp', groupPicture, {ends: true});
};

55
scripts/tags/index.js Normal file
View File

@ -0,0 +1,55 @@
/* global hexo */
'use strict';
const postButton = require('./button')(hexo);
hexo.extend.tag.register('button', postButton);
hexo.extend.tag.register('btn', postButton);
const caniUse = require('./caniuse')(hexo);
hexo.extend.tag.register('caniuse', caniUse);
hexo.extend.tag.register('can', caniUse);
const centerQuote = require('./center-quote')(hexo);
hexo.extend.tag.register('centerquote', centerQuote, true);
hexo.extend.tag.register('cq', centerQuote, true);
const groupPicture = require('./group-pictures')(hexo);
hexo.extend.tag.register('grouppicture', groupPicture, true);
hexo.extend.tag.register('gp', groupPicture, true);
const postLabel = require('./label')(hexo);
hexo.extend.tag.register('label', postLabel);
const linkGrid = require('./link-grid');
hexo.extend.tag.register('linkgrid', linkGrid, true);
hexo.extend.tag.register('lg', linkGrid, true);
const mermaid = require('./mermaid');
hexo.extend.tag.register('mermaid', mermaid, true);
const postNote = require('./note')(hexo);
hexo.extend.tag.register('note', postNote, true);
hexo.extend.tag.register('subnote', postNote, true);
const pdf = require('./pdf')(hexo);
hexo.extend.tag.register('pdf', pdf);
const postTabs = require('./tabs')(hexo);
hexo.extend.tag.register('tabs', postTabs, true);
hexo.extend.tag.register('subtabs', postTabs, true);
hexo.extend.tag.register('subsubtabs', postTabs, true);
const postVideo = require('./video');
hexo.extend.tag.register('video', postVideo);

View File

@ -2,18 +2,14 @@
* label.js | https://theme-next.js.org/docs/tag-plugins/label
*/
/* global hexo */
'use strict';
function postLabel(args) {
module.exports = ctx => function(args) {
args = args.join(' ').split('@');
const classes = args[0] || 'default';
const text = args[1] || '';
if (!text) hexo.log.warn('Label text must be defined!');
if (!text) ctx.log.warn('Label text must be defined!');
return `<mark class="label ${classes.trim()}">${text}</mark>`;
}
hexo.extend.tag.register('label', postLabel, {ends: false});
};

View File

@ -2,17 +2,15 @@
* link-grid.js | https://theme-next.js.org/docs/tag-plugins/link-grid
*/
/* global hexo */
'use strict';
function linkGrid(args, content) {
module.exports = function(args, content) {
const image = args[0] || '/images/avatar.gif';
const delimiter = args[1] || '|';
const comment = args[2] || '%';
const links = content.split('\n').map(item => {
item = item.split(delimiter).map(arg => arg.trim());
const links = content.split('\n').filter(line => line.trim() !== '').map(line => {
const item = line.split(delimiter).map(arg => arg.trim());
if (item[0][0] === comment) return '';
return `<div class="link-grid-container">
<div class="link-grid-image" style="background-image: url(${item[3] || image});"></div>
@ -21,7 +19,4 @@ function linkGrid(args, content) {
</div>`;
});
return `<div class="link-grid">${links.join('')}</div>`;
}
hexo.extend.tag.register('linkgrid', linkGrid, {ends: true});
hexo.extend.tag.register('lg', linkGrid, {ends: true});
};

View File

@ -2,15 +2,11 @@
* mermaid.js | https://theme-next.js.org/docs/tag-plugins/mermaid
*/
/* global hexo */
'use strict';
function mermaid(args, content) {
module.exports = function(args, content) {
return `<pre class="mermaid" style="text-align: center;">
${args.join(' ')}
${content}
</pre>`;
}
hexo.extend.tag.register('mermaid', mermaid, {ends: true});
};

View File

@ -2,11 +2,9 @@
* note.js | https://theme-next.js.org/docs/tag-plugins/note
*/
/* global hexo */
'use strict';
function postNote(args, content) {
module.exports = ctx => function(args, content) {
const keywords = ['default', 'primary', 'info', 'success', 'warning', 'danger', 'no-icon'];
const className = [];
const summary = [];
@ -17,14 +15,11 @@ function postNote(args, content) {
className.push(arg);
}
});
content = hexo.render.renderSync({ text: content, engine: 'markdown' });
content = ctx.render.renderSync({ text: content, engine: 'markdown' });
if (summary.length === 0) {
return `<div class="note ${args.join(' ')}">${content}</div>`;
}
return `<details class="note ${className.join(' ')}"><summary>${hexo.render.renderSync({ text: summary.join(' '), engine: 'markdown' })}</summary>
return `<details class="note ${className.join(' ')}"><summary>${ctx.render.renderSync({ text: summary.join(' '), engine: 'markdown' })}</summary>
${content}
</details>`;
}
hexo.extend.tag.register('note', postNote, {ends: true});
hexo.extend.tag.register('subnote', postNote, {ends: true});
};

View File

@ -2,13 +2,9 @@
* pdf.js | https://theme-next.js.org/docs/tag-plugins/pdf
*/
/* global hexo */
'use strict';
function pdf(args) {
const theme = hexo.theme.config;
module.exports = ctx => function(args) {
const theme = ctx.theme.config;
return `<div class="pdfobject-container" data-target="${args[0]}" data-height="${args[1] || theme.pdf.height}"></div>`;
}
hexo.extend.tag.register('pdf', pdf, {ends: false});
};

View File

@ -2,11 +2,9 @@
* tabs.js | https://theme-next.js.org/docs/tag-plugins/tabs
*/
/* global hexo */
'use strict';
function postTabs(args, content) {
module.exports = ctx => function(args, content) {
const tabBlock = /<!--\s*tab (.*?)\s*-->\n([\w\W\s\S]*?)<!--\s*endtab\s*-->/g;
args = args.join(' ').split(',');
@ -19,7 +17,7 @@ function postTabs(args, content) {
let tabNav = '';
let tabContent = '';
if (!tabName) hexo.log.warn('Tabs block must have unique name!');
if (!tabName) ctx.log.warn('Tabs block must have unique name!');
while ((match = tabBlock.exec(content)) !== null) {
matches.push(match[1]);
@ -30,7 +28,7 @@ function postTabs(args, content) {
let [caption = '', icon = ''] = matches[i].split('@');
let postContent = matches[i + 1];
postContent = hexo.render.renderSync({text: postContent, engine: 'markdown'}).trim();
postContent = ctx.render.renderSync({ text: postContent, engine: 'markdown' }).trim();
const abbr = tabName + ' ' + ++tabId;
const href = abbr.toLowerCase().split(' ').join('-');
@ -52,8 +50,4 @@ function postTabs(args, content) {
tabContent = `<div class="tab-content">${tabContent}</div>`;
return `<div class="tabs" id="${tabName.toLowerCase().split(' ').join('-')}">${tabNav + tabContent}</div>`;
}
hexo.extend.tag.register('tabs', postTabs, {ends: true});
hexo.extend.tag.register('subtabs', postTabs, {ends: true});
hexo.extend.tag.register('subsubtabs', postTabs, {ends: true});
};

View File

@ -2,12 +2,8 @@
* video.js | https://theme-next.js.org/docs/tag-plugins/
*/
/* global hexo */
'use strict';
function postVideo(args) {
module.exports = function(args) {
return `<video src="${args}" preload="metadata" controls playsinline poster="">Sorry, your browser does not support the video tag.</video>`;
}
hexo.extend.tag.register('video', postVideo, {ends: false});
};

86
test/helpers/font.js Normal file
View File

@ -0,0 +1,86 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
const fontStyles = ':300,300italic,400,400italic,700,700italic';
const fontHost = '//fonts.googleapis.com';
describe('font', () => {
const nextFont = require('../../scripts/helpers/font').bind(hexo);
before(() => {
hexo.theme.font = {};
});
it('font disabled', () => {
hexo.theme.font.enable = false;
hexo.theme.font.title = {
family : 'Amatic SC',
external: true
};
nextFont().should.eql('');
});
it('no external font', () => {
hexo.theme.font.enable = true;
hexo.theme.font.title = {
family : 'Amatic SC',
external: false
};
nextFont().should.eql('');
});
it('trivial', () => {
hexo.theme.font.enable = true;
hexo.theme.font.title = {
family : 'Amatic SC',
external: true
};
hexo.theme.font.headings = {
family : 'Palatino',
external: false
};
nextFont().should.eql(`<link rel="stylesheet" href="${fontHost}/css?family=Amatic+SC${fontStyles}&display=swap&subset=latin,latin-ext">`);
});
it('multiple', () => {
hexo.theme.font.enable = true;
hexo.theme.font.title = {
family : 'Amatic SC',
external: true
};
hexo.theme.font.headings = {
family : 'Palatino',
external: true
};
nextFont().should.eql(`<link rel="stylesheet" href="${fontHost}/css?family=Amatic+SC${fontStyles}|Palatino${fontStyles}&display=swap&subset=latin,latin-ext">`);
});
it('duplicate', () => {
hexo.theme.font.enable = true;
hexo.theme.font.title = {
family : 'Palatino',
external: true
};
hexo.theme.font.headings = {
family : 'Palatino',
external: true
};
nextFont().should.eql(`<link rel="stylesheet" href="${fontHost}/css?family=Palatino${fontStyles}&display=swap&subset=latin,latin-ext">`);
});
it('fallback font', () => {
hexo.theme.font.enable = true;
hexo.theme.font.title = {
family : 'Roboto Slab, Noto Serif SC',
external: true
};
hexo.theme.font.headings = {
family : 'Palatino',
external: true
};
nextFont().should.eql(`<link rel="stylesheet" href="${fontHost}/css?family=Roboto+Slab${fontStyles}|Noto+Serif+SC${fontStyles}|Palatino${fontStyles}&display=swap&subset=latin,latin-ext">`);
});
});

6
test/helpers/index.js Normal file
View File

@ -0,0 +1,6 @@
'use strict';
describe('Helpers', () => {
require('./font');
require('./next-url');
});

40
test/helpers/next-url.js Normal file
View File

@ -0,0 +1,40 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
function btoa(str) {
return Buffer.from(str).toString('base64');
}
describe('next-url', () => {
const nextUrl = require('../../scripts/helpers/next-url').bind(hexo);
before(() => {
hexo.config.url = 'https://example.com';
hexo.url_for = require('hexo/lib/plugins/helper/url_for').bind(hexo);
});
it('text', () => {
nextUrl('/child/', 'Text').should.eql('<a href="/child/">Text</a>');
});
it('icon', () => {
nextUrl('/child/', '<i class="fab fa-fort-awesome"></i>').should.eql('<a href="/child/"><i class="fab fa-fort-awesome"></i></a>');
});
it('class', () => {
nextUrl('/child/', 'Text', { class: 'theme-link' }).should.eql('<a href="/child/" class="theme-link">Text</a>');
});
it('external', () => {
nextUrl('https://theme-next.js.org', 'Text').should.eql('<a href="https://theme-next.js.org/" rel="noopener" target="_blank">Text</a>');
});
it('exturl enabled', () => {
hexo.theme.exturl = true;
const encoded = btoa('https://theme-next.js.org');
nextUrl('https://theme-next.js.org', 'Text').should.eql(`<span class="exturl" data-url="${encoded}">Text</span>`);
});
});

7
test/index.js vendored Normal file
View File

@ -0,0 +1,7 @@
'use strict';
describe('NexT', () => {
require('./helpers');
require('./tags');
require('./validate');
});

33
test/tags/button.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
describe('button', () => {
const postButton = require('../../scripts/tags/button')(hexo);
it('only url', () => {
postButton(['#']).should.eql('<a class="btn" href="#"></a>');
});
it('url and text', () => {
postButton('#, Hello world'.split(' ')).should.eql('<a class="btn" href="#">Hello world</a>');
});
it('url and icon (Font Awesome 4)', () => {
postButton('#,, home fa-5x'.split(' ')).should.eql('<a class="btn" href="#"><i class="fa fa-home fa-5x"></i></a>');
});
it('url and icon', () => {
postButton('#,, fab fa-fort-awesome fa-5x'.split(' ')).should.eql('<a class="btn" href="#"><i class="fab fa-fort-awesome fa-5x"></i></a>');
});
it('url, text and title', () => {
postButton('#, Hello world,, Title'.split(' ')).should.eql('<a class="btn" href="#" title="Title">Hello world</a>');
});
it('url, text, icon and title', () => {
postButton('#, Hello world, home, Title'.split(' ')).should.eql('<a class="btn" href="#" title="Title"><i class="fa fa-home"></i>Hello world</a>');
});
});

17
test/tags/caniuse.js Normal file
View File

@ -0,0 +1,17 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
describe('caniuse', () => {
const caniUse = require('../../scripts/tags/caniuse')(hexo);
it('only feature', () => {
caniUse(['loading-lazy-attr']).should.eql('<iframe data-feature="loading-lazy-attr" src="https://caniuse.bitsofco.de/embed/index.html?feat=loading-lazy-attr&periods=current&accessible-colours=false" frameborder="0" width="100%" height="400px"></iframe>');
});
it('feature and periods', () => {
caniUse('fetch @ future_3,future_2,future_1'.split(' ')).should.eql('<iframe data-feature="fetch" src="https://caniuse.bitsofco.de/embed/index.html?feat=fetch&periods=future_3,future_2,future_1&accessible-colours=false" frameborder="0" width="100%" height="400px"></iframe>');
});
});

21
test/tags/center-quote.js Normal file
View File

@ -0,0 +1,21 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
const content = 'Test **Bold** *Italic*';
const result = '<p>Test <strong>Bold</strong> <em>Italic</em></p>';
describe('center-quote', () => {
const centerQuote = require('../../scripts/tags/center-quote')(hexo);
before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked'))));
it('markdown content', () => {
centerQuote([], content).should.eql(`<blockquote class="blockquote-center">
${result}
</blockquote>`);
});
});

View File

@ -0,0 +1,45 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
describe('group-pictures', () => {
const groupPicture = require('../../scripts/tags/group-pictures')(hexo);
before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked'))));
it('layout 3-1', () => {
groupPicture(['3-1'], `
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)`).should.eql('<div class="group-picture"><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div></div></div>');
});
it('layout 5-2', () => {
groupPicture(['5-2'], `
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)`).should.eql('<div class="group-picture"><div class="group-picture-row"><div class="group-picture-column" style="width: 50%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 50%;"><img src="/images/sample.png"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 100%;"><img src="/images/sample.png"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 50%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 50%;"><img src="/images/sample.png"></div></div></div>');
});
it('remove text', () => {
groupPicture(['3-1'], `
![](/images/sample.png)
Text
![](/images/sample.png)
Text
![](/images/sample.png)`).should.eql('<div class="group-picture"><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div></div></div>');
});
it('no layout', () => {
groupPicture(['NaN-NaN'], `
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)
![](/images/sample.png)`).should.eql('<div class="group-picture"><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img src="/images/sample.png"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 50%;"><img src="/images/sample.png"></div><div class="group-picture-column" style="width: 50%;"><img src="/images/sample.png"></div></div></div>');
});
});

15
test/tags/index.js Normal file
View File

@ -0,0 +1,15 @@
'use strict';
describe('Tags', () => {
require('./button');
require('./caniuse');
require('./center-quote');
require('./group-pictures');
require('./label');
require('./link-grid');
require('./mermaid');
require('./note');
require('./pdf');
require('./tabs');
require('./video');
});

17
test/tags/label.js Normal file
View File

@ -0,0 +1,17 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
describe('label', () => {
const postLabel = require('../../scripts/tags/label')(hexo);
it('only text', () => {
postLabel('@Hello world'.split(' ')).should.eql('<mark class="label default">Hello world</mark>');
});
it('classes and text', () => {
postLabel('primary@Hello world'.split(' ')).should.eql('<mark class="label primary">Hello world</mark>');
});
});

49
test/tags/link-grid.js Normal file
View File

@ -0,0 +1,49 @@
'use strict';
require('chai').should();
const result = `<div class="link-grid"><div class="link-grid-container">
<div class="link-grid-image" style="background-image: url(/images/sample.png);"></div>
<p>Theme NexT</p><p>Stay Simple. Stay NexT.</p>
<a href="https://theme-next.js.org/"></a>
</div><div class="link-grid-container">
<div class="link-grid-image" style="background-image: url(/images/sample.png);"></div>
<p>Theme NexT</p><p>Stay Simple. Stay NexT.</p>
<a href="https://theme-next.js.org/"></a>
</div></div>`;
describe('link-grid', () => {
const linkGrid = require('../../scripts/tags/link-grid');
it('default', () => {
linkGrid([], `
Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png
Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png`).should.eql(result);
});
it('comment', () => {
linkGrid([], `
Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png
Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png
% Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png`).should.eql(result);
});
it('default image', () => {
linkGrid(['/images/sample.png'], `
Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. |
Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. |`).should.eql(result);
});
it('custom delimiter', () => {
linkGrid(['/images/sample.png', ','], `
Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png
Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png`).should.eql(result);
});
it('custom delimiter and comment', () => {
linkGrid(['/images/sample.png', ',', '#'], `
Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png
Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png
# Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png`).should.eql(result);
});
});

19
test/tags/mermaid.js Normal file
View File

@ -0,0 +1,19 @@
'use strict';
require('chai').should();
const result = `A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]`;
describe('mermaid', () => {
const mermaid = require('../../scripts/tags/mermaid');
it('default', () => {
mermaid(['graph', 'TD'], result).should.eql(`<pre class="mermaid" style="text-align: center;">
graph TD
${result}
</pre>`);
});
});

55
test/tags/note.js Normal file
View File

@ -0,0 +1,55 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
const content = 'Test **Bold** *Italic*';
const result = '<p>Test <strong>Bold</strong> <em>Italic</em></p>';
const args = 'This is a *summary*'.split(' ');
const summary = '<summary><p>This is a <em>summary</em>';
describe('note', () => {
const postNote = require('../../scripts/tags/note')(hexo);
before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked'))));
it('only text', () => {
postNote([], content).should.eql(`<div class="note ">${result}
</div>`);
});
it('classes and text', () => {
postNote(['primary'], content).should.eql(`<div class="note primary">${result}
</div>`);
});
it('classes, no-icon and text', () => {
postNote(['primary', 'no-icon'], content).should.eql(`<div class="note primary no-icon">${result}
</div>`);
});
it('summary and text', () => {
postNote(args, content).should.eql(`<details class="note ">${summary}</p>
</summary>
${result}
</details>`);
});
it('classes, summary and text', () => {
postNote(['primary'].concat(args), content).should.eql(`<details class="note primary">${summary}</p>
</summary>
${result}
</details>`);
});
it('classes, no-icon, summary and text', () => {
postNote(['primary', 'no-icon'].concat(args), content).should.eql(`<details class="note primary no-icon">${summary}</p>
</summary>
${result}
</details>`);
});
});

23
test/tags/pdf.js Normal file
View File

@ -0,0 +1,23 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
describe('pdf', () => {
const pdf = require('../../scripts/tags/pdf')(hexo);
before(() => {
hexo.theme.config.pdf = {
height: '500px'
};
});
it('default', () => {
pdf(['https://example.com/sample.pdf']).should.eql('<div class="pdfobject-container" data-target="https://example.com/sample.pdf" data-height="500px"></div>');
});
it('custom height', () => {
pdf(['https://example.com/sample.pdf', '1000px']).should.eql('<div class="pdfobject-container" data-target="https://example.com/sample.pdf" data-height="1000px"></div>');
});
});

96
test/tags/tabs.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
require('chai').should();
const Hexo = require('hexo');
const hexo = new Hexo();
const content = 'Test **Bold** *Italic*';
const result = '<p>Test <strong>Bold</strong> <em>Italic</em></p>';
const container = '<div class="tabs" id="name"><ul class="nav-tabs">';
describe('tabs', () => {
const postTabs = require('../../scripts/tags/tabs')(hexo);
before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked'))));
it('empty', () => {
postTabs(['name']).should.eql(`${container}</ul><div class="tab-content"></div></div>`);
});
it('default', () => {
postTabs(['name'],
`<!-- tab -->
${content}
<!-- endtab -->
<!-- tab -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab active"><a href="#name-1">name 1</a></li><li class="tab"><a href="#name-2">name 2</a></li></ul><div class="tab-content"><div class="tab-pane active" id="name-1">${result}</div><div class="tab-pane" id="name-2">${result}</div></div></div>`);
});
it('selected index', () => {
postTabs('name, 2'.split(' '),
`<!-- tab -->
${content}
<!-- endtab -->
<!-- tab -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab"><a href="#name-1">name 1</a></li><li class="tab active"><a href="#name-2">name 2</a></li></ul><div class="tab-content"><div class="tab-pane" id="name-1">${result}</div><div class="tab-pane active" id="name-2">${result}</div></div></div>`);
});
it('no tab selected', () => {
postTabs('name, -1'.split(' '),
`<!-- tab -->
${content}
<!-- endtab -->
<!-- tab -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab"><a href="#name-1">name 1</a></li><li class="tab"><a href="#name-2">name 2</a></li></ul><div class="tab-content"><div class="tab-pane" id="name-1">${result}</div><div class="tab-pane" id="name-2">${result}</div></div></div>`);
});
it('label', () => {
postTabs('name'.split(' '),
`<!-- tab Tab 1 -->
${content}
<!-- endtab -->
<!-- tab Tab 2 -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab active"><a href="#name-1">Tab 1</a></li><li class="tab"><a href="#name-2">Tab 2</a></li></ul><div class="tab-content"><div class="tab-pane active" id="name-1">${result}</div><div class="tab-pane" id="name-2">${result}</div></div></div>`);
});
it('icon (Font Awesome 4)', () => {
postTabs('name'.split(' '),
`<!-- tab @home -->
${content}
<!-- endtab -->
<!-- tab @home -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab active"><a href="#name-1"><i class="fa fa-home"></i></a></li><li class="tab"><a href="#name-2"><i class="fa fa-home"></i></a></li></ul><div class="tab-content"><div class="tab-pane active" id="name-1">${result}</div><div class="tab-pane" id="name-2">${result}</div></div></div>`);
});
it('icon', () => {
postTabs('name'.split(' '),
`<!-- tab @fab fa-fort-awesome -->
${content}
<!-- endtab -->
<!-- tab @fab fa-fort-awesome -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab active"><a href="#name-1"><i class="fab fa-fort-awesome"></i></a></li><li class="tab"><a href="#name-2"><i class="fab fa-fort-awesome"></i></a></li></ul><div class="tab-content"><div class="tab-pane active" id="name-1">${result}</div><div class="tab-pane" id="name-2">${result}</div></div></div>`);
});
it('label and icon', () => {
postTabs('name, -1'.split(' '),
`<!-- tab Tab 1@home -->
${content}
<!-- endtab -->
<!-- tab Tab 1@home -->
${content}
<!-- endtab -->`).should.eql(`${container}<li class="tab"><a href="#name-1"><i class="fa fa-home"></i>Tab 1</a></li><li class="tab"><a href="#name-2"><i class="fa fa-home"></i>Tab 1</a></li></ul><div class="tab-content"><div class="tab-pane" id="name-1">${result}</div><div class="tab-pane" id="name-2">${result}</div></div></div>`);
});
});

11
test/tags/video.js Normal file
View File

@ -0,0 +1,11 @@
'use strict';
require('chai').should();
describe('video', () => {
const postVideo = require('../../scripts/tags/video');
it('default', () => {
postVideo(['https://example.com/sample.mp4']).should.eql('<video src="https://example.com/sample.mp4" preload="metadata" controls playsinline poster="">Sorry, your browser does not support the video tag.</video>');
});
});

28
test/validate/index.js Normal file
View File

@ -0,0 +1,28 @@
'use strict';
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
const should = require('chai').should();
describe('Validate', () => {
it('config', () => {
const themeConfig = fs.readFileSync(path.join(__dirname, '../../_config.yml'));
should.not.throw(() => {
yaml.safeLoad(themeConfig);
});
});
it('language', () => {
const languagesPath = path.join(__dirname, '../../languages');
should.not.throw(() => {
fs.readdirSync(languagesPath).forEach(lang => {
if (!lang.endsWith('.yml')) return;
const languagePath = path.join(languagesPath, lang);
yaml.safeLoad(fs.readFileSync(languagePath), {
filename: path.relative(__dirname, languagePath)
});
});
});
});
});