diff --git a/.gitattributes b/.gitattributes index 620b56b..34c7ad6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ source/lib/* linguist-vendored +test/* linguist-vendored diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index c3d3877..1b04287 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -10,4 +10,5 @@ jobs: - name: Use Node.js uses: actions/setup-node@v1 - run: npm install + - run: npm run eslint - run: npm test diff --git a/.npmignore b/.npmignore index 0b9f150..ecb49b3 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ .github/ +test/ .editorconfig .eslintrc.json .gitattributes diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 6a39a1b..0000000 --- a/gulpfile.js +++ /dev/null @@ -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')); diff --git a/package.json b/package.json index f4f6009..b42a0ca 100644 --- a/package.json +++ b/package.json @@ -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" } } } diff --git a/scripts/helpers/engine.js b/scripts/helpers/engine.js index fdcb18d..2144ab2 100644 --- a/scripts/helpers/engine.js +++ b/scripts/helpers/engine.js @@ -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] diff --git a/scripts/helpers/font.js b/scripts/helpers/font.js index 27527f7..57b95bf 100644 --- a/scripts/helpers/font.js +++ b/scripts/helpers/font.js @@ -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 ? `` : ''; -}); +}; diff --git a/scripts/helpers/next-url.js b/scripts/helpers/next-url.js index 7212b44..240459a 100644 --- a/scripts/helpers/next-url.js +++ b/scripts/helpers/next-url.js @@ -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); -}); +}; diff --git a/scripts/tags/button.js b/scripts/tags/button.js index 21c5b4d..8379ffd 100644 --- a/scripts/tags/button.js +++ b/scripts/tags/button.js @@ -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 ` 0 ? ` title="${title}"` : ''}>${icon}${text}`; -} - -hexo.extend.tag.register('button', postButton, {ends: false}); -hexo.extend.tag.register('btn', postButton, {ends: false}); +}; diff --git a/scripts/tags/caniuse.js b/scripts/tags/caniuse.js index 18cfe70..ec0e549 100644 --- a/scripts/tags/caniuse.js +++ b/scripts/tags/caniuse.js @@ -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 ``; -} - -hexo.extend.tag.register('caniuse', caniUse); -hexo.extend.tag.register('can', caniUse); +}; diff --git a/scripts/tags/center-quote.js b/scripts/tags/center-quote.js index 425d531..169be33 100644 --- a/scripts/tags/center-quote.js +++ b/scripts/tags/center-quote.js @@ -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 `
-${hexo.render.renderSync({ text: content, engine: 'markdown' })} +${ctx.render.renderSync({ text: content, engine: 'markdown' })}
`; -} - -hexo.extend.tag.register('centerquote', centerQuote, {ends: true}); -hexo.extend.tag.register('cq', centerQuote, {ends: true}); +}; diff --git a/scripts/tags/group-pictures.js b/scripts/tags/group-pictures.js index 0ab46e8..4c20c73 100644 --- a/scripts/tags/group-pictures.js +++ b/scripts/tags/group-pictures.js @@ -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(//g); return `
${templates.dispatch(pictures, group, layout)}
`; -} - -hexo.extend.tag.register('grouppicture', groupPicture, {ends: true}); -hexo.extend.tag.register('gp', groupPicture, {ends: true}); +}; diff --git a/scripts/tags/index.js b/scripts/tags/index.js new file mode 100644 index 0000000..fe06950 --- /dev/null +++ b/scripts/tags/index.js @@ -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); diff --git a/scripts/tags/label.js b/scripts/tags/label.js index efab944..80f3c27 100644 --- a/scripts/tags/label.js +++ b/scripts/tags/label.js @@ -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 `${text}`; -} - -hexo.extend.tag.register('label', postLabel, {ends: false}); +}; diff --git a/scripts/tags/link-grid.js b/scripts/tags/link-grid.js index da83cf8..c95421e 100644 --- a/scripts/tags/link-grid.js +++ b/scripts/tags/link-grid.js @@ -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 ``; }); return ``; -} - -hexo.extend.tag.register('linkgrid', linkGrid, {ends: true}); -hexo.extend.tag.register('lg', linkGrid, {ends: true}); +}; diff --git a/scripts/tags/mermaid.js b/scripts/tags/mermaid.js index 2071568..e2a029c 100644 --- a/scripts/tags/mermaid.js +++ b/scripts/tags/mermaid.js @@ -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 `
 ${args.join(' ')}
 ${content}
 
`; -} - -hexo.extend.tag.register('mermaid', mermaid, {ends: true}); +}; diff --git a/scripts/tags/note.js b/scripts/tags/note.js index cfc0857..1c81c4a 100644 --- a/scripts/tags/note.js +++ b/scripts/tags/note.js @@ -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 `
${content}
`; } - return `
${hexo.render.renderSync({ text: summary.join(' '), engine: 'markdown' })} + return `
${ctx.render.renderSync({ text: summary.join(' '), engine: 'markdown' })} ${content}
`; -} - -hexo.extend.tag.register('note', postNote, {ends: true}); -hexo.extend.tag.register('subnote', postNote, {ends: true}); +}; diff --git a/scripts/tags/pdf.js b/scripts/tags/pdf.js index 78303ca..8145365 100644 --- a/scripts/tags/pdf.js +++ b/scripts/tags/pdf.js @@ -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 `
`; -} - -hexo.extend.tag.register('pdf', pdf, {ends: false}); +}; diff --git a/scripts/tags/tabs.js b/scripts/tags/tabs.js index e2e3432..8611f6e 100644 --- a/scripts/tags/tabs.js +++ b/scripts/tags/tabs.js @@ -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 = /\n([\w\W\s\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 = `
${tabContent}
`; return `
${tabNav + tabContent}
`; -} - -hexo.extend.tag.register('tabs', postTabs, {ends: true}); -hexo.extend.tag.register('subtabs', postTabs, {ends: true}); -hexo.extend.tag.register('subsubtabs', postTabs, {ends: true}); +}; diff --git a/scripts/tags/video.js b/scripts/tags/video.js index 3452ebf..9f4e70c 100644 --- a/scripts/tags/video.js +++ b/scripts/tags/video.js @@ -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 ``; -} - -hexo.extend.tag.register('video', postVideo, {ends: false}); +}; diff --git a/test/helpers/font.js b/test/helpers/font.js new file mode 100644 index 0000000..e1eeb97 --- /dev/null +++ b/test/helpers/font.js @@ -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(``); + }); + + 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(``); + }); + + 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(``); + }); + + 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(``); + }); +}); diff --git a/test/helpers/index.js b/test/helpers/index.js new file mode 100644 index 0000000..3ef5dde --- /dev/null +++ b/test/helpers/index.js @@ -0,0 +1,6 @@ +'use strict'; + +describe('Helpers', () => { + require('./font'); + require('./next-url'); +}); diff --git a/test/helpers/next-url.js b/test/helpers/next-url.js new file mode 100644 index 0000000..a9fef70 --- /dev/null +++ b/test/helpers/next-url.js @@ -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('Text'); + }); + + it('icon', () => { + nextUrl('/child/', '').should.eql(''); + }); + + it('class', () => { + nextUrl('/child/', 'Text', { class: 'theme-link' }).should.eql('Text'); + }); + + it('external', () => { + nextUrl('https://theme-next.js.org', 'Text').should.eql('Text'); + }); + + it('exturl enabled', () => { + hexo.theme.exturl = true; + const encoded = btoa('https://theme-next.js.org'); + nextUrl('https://theme-next.js.org', 'Text').should.eql(`Text`); + }); +}); diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..e6088b6 --- /dev/null +++ b/test/index.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('NexT', () => { + require('./helpers'); + require('./tags'); + require('./validate'); +}); diff --git a/test/tags/button.js b/test/tags/button.js new file mode 100644 index 0000000..f74583a --- /dev/null +++ b/test/tags/button.js @@ -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(''); + }); + + it('url and text', () => { + postButton('#, Hello world'.split(' ')).should.eql('Hello world'); + }); + + it('url and icon (Font Awesome 4)', () => { + postButton('#,, home fa-5x'.split(' ')).should.eql(''); + }); + + it('url and icon', () => { + postButton('#,, fab fa-fort-awesome fa-5x'.split(' ')).should.eql(''); + }); + + it('url, text and title', () => { + postButton('#, Hello world,, Title'.split(' ')).should.eql('Hello world'); + }); + + it('url, text, icon and title', () => { + postButton('#, Hello world, home, Title'.split(' ')).should.eql('Hello world'); + }); +}); diff --git a/test/tags/caniuse.js b/test/tags/caniuse.js new file mode 100644 index 0000000..1fe0e20 --- /dev/null +++ b/test/tags/caniuse.js @@ -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(''); + }); + + it('feature and periods', () => { + caniUse('fetch @ future_3,future_2,future_1'.split(' ')).should.eql(''); + }); +}); diff --git a/test/tags/center-quote.js b/test/tags/center-quote.js new file mode 100644 index 0000000..0f20488 --- /dev/null +++ b/test/tags/center-quote.js @@ -0,0 +1,21 @@ +'use strict'; + +require('chai').should(); +const Hexo = require('hexo'); +const hexo = new Hexo(); + +const content = 'Test **Bold** *Italic*'; +const result = '

Test Bold Italic

'; + +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(`
+${result} + +
`); + }); +}); diff --git a/test/tags/group-pictures.js b/test/tags/group-pictures.js new file mode 100644 index 0000000..c2b6695 --- /dev/null +++ b/test/tags/group-pictures.js @@ -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('
'); + }); + + it('layout 5-2', () => { + groupPicture(['5-2'], ` +![](/images/sample.png) +![](/images/sample.png) +![](/images/sample.png) +![](/images/sample.png) +![](/images/sample.png)`).should.eql('
'); + }); + + it('remove text', () => { + groupPicture(['3-1'], ` +![](/images/sample.png) +Text +![](/images/sample.png) +Text +![](/images/sample.png)`).should.eql('
'); + }); + + it('no layout', () => { + groupPicture(['NaN-NaN'], ` +![](/images/sample.png) +![](/images/sample.png) +![](/images/sample.png) +![](/images/sample.png) +![](/images/sample.png)`).should.eql('
'); + }); +}); diff --git a/test/tags/index.js b/test/tags/index.js new file mode 100644 index 0000000..77806a4 --- /dev/null +++ b/test/tags/index.js @@ -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'); +}); diff --git a/test/tags/label.js b/test/tags/label.js new file mode 100644 index 0000000..bf3c011 --- /dev/null +++ b/test/tags/label.js @@ -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('Hello world'); + }); + + it('classes and text', () => { + postLabel('primary@Hello world'.split(' ')).should.eql('Hello world'); + }); +}); diff --git a/test/tags/link-grid.js b/test/tags/link-grid.js new file mode 100644 index 0000000..4dacb52 --- /dev/null +++ b/test/tags/link-grid.js @@ -0,0 +1,49 @@ +'use strict'; + +require('chai').should(); + +const result = ``; + +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); + }); +}); diff --git a/test/tags/mermaid.js b/test/tags/mermaid.js new file mode 100644 index 0000000..546e75a --- /dev/null +++ b/test/tags/mermaid.js @@ -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(`
+graph TD
+${result}
+
`); + }); +}); diff --git a/test/tags/note.js b/test/tags/note.js new file mode 100644 index 0000000..3fff75c --- /dev/null +++ b/test/tags/note.js @@ -0,0 +1,55 @@ +'use strict'; + +require('chai').should(); +const Hexo = require('hexo'); +const hexo = new Hexo(); + +const content = 'Test **Bold** *Italic*'; +const result = '

Test Bold Italic

'; +const args = 'This is a *summary*'.split(' '); +const summary = '

This is a summary'; + +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(`

${result} +
`); + }); + + it('classes and text', () => { + postNote(['primary'], content).should.eql(`
${result} +
`); + }); + + it('classes, no-icon and text', () => { + postNote(['primary', 'no-icon'], content).should.eql(`
${result} +
`); + }); + + it('summary and text', () => { + postNote(args, content).should.eql(`
${summary}

+
+${result} + +
`); + }); + + it('classes, summary and text', () => { + postNote(['primary'].concat(args), content).should.eql(`
${summary}

+ +${result} + +
`); + }); + + it('classes, no-icon, summary and text', () => { + postNote(['primary', 'no-icon'].concat(args), content).should.eql(`
${summary}

+ +${result} + +
`); + }); +}); diff --git a/test/tags/pdf.js b/test/tags/pdf.js new file mode 100644 index 0000000..6186677 --- /dev/null +++ b/test/tags/pdf.js @@ -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('
'); + }); + + it('custom height', () => { + pdf(['https://example.com/sample.pdf', '1000px']).should.eql('
'); + }); +}); diff --git a/test/tags/tabs.js b/test/tags/tabs.js new file mode 100644 index 0000000..db09ab1 --- /dev/null +++ b/test/tags/tabs.js @@ -0,0 +1,96 @@ +'use strict'; + +require('chai').should(); +const Hexo = require('hexo'); +const hexo = new Hexo(); + +const content = 'Test **Bold** *Italic*'; +const result = '

Test Bold Italic

'; +const container = '
`); + }); + + it('default', () => { + postTabs(['name'], + ` +${content} + + + +${content} +`).should.eql(`${container}
  • name 1
  • name 2
  • ${result}
    ${result}
    `); + }); + + it('selected index', () => { + postTabs('name, 2'.split(' '), + ` +${content} + + + +${content} +`).should.eql(`${container}
  • name 1
  • name 2
  • ${result}
    ${result}
    `); + }); + + it('no tab selected', () => { + postTabs('name, -1'.split(' '), + ` +${content} + + + +${content} +`).should.eql(`${container}
  • name 1
  • name 2
  • ${result}
    ${result}
    `); + }); + + it('label', () => { + postTabs('name'.split(' '), + ` +${content} + + + +${content} +`).should.eql(`${container}
  • Tab 1
  • Tab 2
  • ${result}
    ${result}
    `); + }); + + it('icon (Font Awesome 4)', () => { + postTabs('name'.split(' '), + ` +${content} + + + +${content} +`).should.eql(`${container}
  • ${result}
    ${result}
    `); + }); + + it('icon', () => { + postTabs('name'.split(' '), + ` +${content} + + + +${content} +`).should.eql(`${container}
  • ${result}
    ${result}
    `); + }); + + it('label and icon', () => { + postTabs('name, -1'.split(' '), + ` +${content} + + + +${content} +`).should.eql(`${container}
  • Tab 1
  • Tab 1
  • ${result}
    ${result}
    `); + }); +}); diff --git a/test/tags/video.js b/test/tags/video.js new file mode 100644 index 0000000..4e56cdb --- /dev/null +++ b/test/tags/video.js @@ -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(''); + }); +}); diff --git a/test/validate/index.js b/test/validate/index.js new file mode 100644 index 0000000..ccc67f2 --- /dev/null +++ b/test/validate/index.js @@ -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) + }); + }); + }); + }); +});