From 86ad7f314900a98de9efa90cdce4cd8a21d57468 Mon Sep 17 00:00:00 2001 From: koy Date: Fri, 19 Jul 2024 21:30:43 +0800 Subject: [PATCH 01/25] fix: fix loadSider false render structure issue --- src/core/fetch/index.js | 15 ++-- src/core/render/compiler.js | 16 +++-- src/core/render/index.js | 4 +- src/core/render/tpl.js | 7 +- test/e2e/configuration.test.js | 10 +-- test/integration/sidebar.test.js | 116 +++++++++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 23 deletions(-) create mode 100644 test/integration/sidebar.test.js diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js index 8c6cc8356..bc14eeb4f 100644 --- a/src/core/fetch/index.js +++ b/src/core/fetch/index.js @@ -67,17 +67,18 @@ export function Fetch(Base) { _loadSideAndNav(path, qs, loadSidebar, cb) { return () => { - if (!loadSidebar) { - return cb(); - } - - const fn = result => { + const renderSidebar = result => { this._renderSidebar(result); cb(); }; - // Load sidebar - this.#loadNested(path, qs, loadSidebar, fn, this, true); + if (!loadSidebar) { + // Although, we don't load sidebar from sidebar file, we still need call the render to auto generate sidebar from headings toc + renderSidebar(); + } + + // Load sidebar from the sidebar file + this.#loadNested(path, qs, loadSidebar, renderSidebar, this, true); }; } diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index 99c639e11..9b4b6df1a 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -251,8 +251,8 @@ export class Compiler { } /** - * Compile sidebar - * @param {String} text Text content + * Compile sidebar, it uses _sidebar.md ( or specific file) or the content's headings toc to render sidebar. + * @param {String} text Text content from the sidebar file, maybe empty * @param {Number} level Type of heading (h tag) * @returns {String} Sidebar element */ @@ -289,17 +289,19 @@ export class Compiler { return html; } + /** + * When current content redirect to a new path file, clean pre content headings toc + */ + resetToc() { + this.toc = []; + } + /** * Compile sub sidebar * @param {Number} level Type of heading (h tag) * @returns {String} Sub-sidebar element */ subSidebar(level) { - if (!level) { - this.toc = []; - return; - } - const currentPath = this.router.getCurrentPath(); const { cacheTree, toc } = this; diff --git a/src/core/render/index.js b/src/core/render/index.js index bf2f859d6..9e4089bd6 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -298,6 +298,7 @@ export function Render(Base) { } this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel)); + sidebarToggleEl.setAttribute('aria-expanded', !isMobile); const activeElmHref = this.router.toURL(this.route.path); @@ -309,8 +310,7 @@ export function Render(Base) { activeEl.parentNode.innerHTML += this.compiler.subSidebar(subMaxLevel) || ''; } else { - // Reset toc - this.compiler.subSidebar(); + this.compiler.resetToc(); } // Bind event diff --git a/src/core/render/tpl.js b/src/core/render/tpl.js index e76eb0595..19a054b34 100644 --- a/src/core/render/tpl.js +++ b/src/core/render/tpl.js @@ -104,10 +104,13 @@ export function tree( let innerHTML = ''; toc.forEach(node => { const title = node.title.replace(/(<([^>]+)>)/g, ''); - innerHTML += /* html */ `
  • ${node.title}
  • `; + let current = `
  • ${node.title}
  • `; if (node.children) { - innerHTML += tree(node.children, tpl); + // when current node has children, we need put them all in parent's
  • block + const children = tree(node.children, tpl); + current = `
  • ${node.title}${children}
  • `; } + innerHTML += current; }); return tpl.replace('{inner}', innerHTML); } diff --git a/test/e2e/configuration.test.js b/test/e2e/configuration.test.js index 1df583c2c..fddc04636 100644 --- a/test/e2e/configuration.test.js +++ b/test/e2e/configuration.test.js @@ -5,10 +5,10 @@ import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Configuration options', () => { test.describe('catchPluginErrors', () => { test('true (handles uncaught errors)', async ({ page }) => { - let consoleMsg, errorMsg; + // let consoleMsg, errorMsg; - page.on('console', msg => (consoleMsg = msg.text())); - page.on('pageerror', err => (errorMsg = err.message)); + // page.on('console', msg => (consoleMsg = msg.text())); + // page.on('pageerror', err => (errorMsg = err.message)); await docsifyInit({ config: { @@ -32,8 +32,8 @@ test.describe('Configuration options', () => { const mainElm = page.locator('#main'); - expect(errorMsg).toBeUndefined(); - expect(consoleMsg).toContain('Docsify plugin error'); + // expect(errorMsg).toBeUndefined(); + // expect(consoleMsg).toContain('Docsify plugin error'); await expect(mainElm).toContainText('Hello World'); await expect(mainElm).toContainText('beforeEach'); }); diff --git a/test/integration/sidebar.test.js b/test/integration/sidebar.test.js new file mode 100644 index 000000000..f2dc0c201 --- /dev/null +++ b/test/integration/sidebar.test.js @@ -0,0 +1,116 @@ +import docsifyInit from '../helpers/docsify-init.js'; +import { expect } from '@playwright/test'; + +describe('Test sidebar render toc structure', function () { + test('Render sidebar with loadSidebar=true and the _sidebar.md file', async () => { + await docsifyInit({ + config: { + loadSidebar: '_sidebar.md', + }, + markdown: { + homepage: '# Hello World', + sidebar: ` + - Getting started + - [Level1](QuickStart.md) + - [Level2](QuickStart2.md) + `, + }, + waitForSelector: '.sidebar-nav > ul', + }); + + const sidebarElm = document.querySelector('.sidebar'); + /** + * Expected render result + * ========================== + *
      + *
    • + * Getting started + * + *
    • + *
    + */ + + const GettingStarted = document.querySelector('.sidebar-nav > ul > li'); + const level1_Elm = document.querySelector( + '.sidebar-nav > ul > li > ul> li', + ); + const level1_A_tag = level1_Elm.querySelector(' a'); + + const level2_Elm = level1_Elm.querySelector(' ul > li '); + + const level2_A_tag = level2_Elm.querySelector(' a'); + + expect(sidebarElm).not.toBeNull(); + expect(GettingStarted).not.toBeNull(); + expect(level1_Elm).not.toBeNull(); + expect(level1_A_tag).not.toBeNull(); + expect(level2_Elm).not.toBeNull(); + expect(level2_A_tag).not.toBeNull(); + expect(level1_A_tag.textContent).toContain('Level1'); + expect(level2_A_tag.textContent).toContain('Level2'); + }); + + test('Render sidebar with loadSidebar=false should be same to loadSidebar=true sidebar structure', async () => { + await docsifyInit({ + config: { + loadSidebar: false, + }, + markdown: { + homepage: ` + # Getting started + some thing + ## Level1 + foo + ### Level2 + bar + `, + }, + waitForSelector: '.sidebar-nav > ul', + }); + + const sidebarElm = document.querySelector('.sidebar'); + /** + * Expected render result + * ========================== + *
      + *
    • + * Getting started + * + *
    • + *
    + */ + + const GettingStarted = document.querySelector('.sidebar-nav > ul > li'); + const level1_Elm = document.querySelector( + '.sidebar-nav > ul > li > ul> li', + ); + const level1_A_tag = level1_Elm.querySelector(' a'); + + const level2_Elm = level1_Elm.querySelector(' ul > li '); + + const level2_A_tag = level2_Elm.querySelector(' a'); + + expect(sidebarElm).not.toBeNull(); + expect(GettingStarted).not.toBeNull(); + expect(level1_Elm).not.toBeNull(); + expect(level1_A_tag).not.toBeNull(); + expect(level2_Elm).not.toBeNull(); + expect(level2_A_tag).not.toBeNull(); + expect(level1_A_tag.textContent).toContain('Level1'); + expect(level2_A_tag.textContent).toContain('Level2'); + }); +}); From 20bf3a6943e0192b8ba0aa494a4ef3aec21fb726 Mon Sep 17 00:00:00 2001 From: koy Date: Sat, 20 Jul 2024 00:46:35 +0800 Subject: [PATCH 02/25] update: resolve merge conflict --- src/core/render/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/render/index.js b/src/core/render/index.js index 51e608c9a..774533c64 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -301,7 +301,6 @@ export function Render(Base) { sidebarToggleEl.setAttribute('aria-expanded', !isMobile()); - const activeElmHref = this.router.toURL(this.route.path); const activeEl = dom.find(`.sidebar-nav a[href="${activeElmHref}"]`); From 0c21a1d09d7842e088d1a31de9e316dfad98c10b Mon Sep 17 00:00:00 2001 From: koy Date: Sat, 20 Jul 2024 00:58:19 +0800 Subject: [PATCH 03/25] chore: fix ci --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 91b865f23..66485e0f5 100644 --- a/package.json +++ b/package.json @@ -108,9 +108,9 @@ "pub": "sh build/release.sh", "serve:dev": "npm run serve -- --dev", "serve": "node server", - "test:e2e": "playwright test", - "test:e2e:chromium": "playwright test --project='chromium'", - "test:e2e:ui": "playwright test --ui", + "test:e2e": "npx playwright test", + "test:e2e:chromium": "npx playwright test --project='chromium'", + "test:e2e:ui": "npx playwright test --ui", "test:integration": "npm run test:jest -- --selectProjects integration", "test:jest": "cross-env NODE_OPTIONS=--experimental-vm-modules jest", "test:unit": "npm run test:jest -- --selectProjects unit", @@ -118,4 +118,4 @@ "watch:css": "run-p 'build:css -- --watch' 'build:css:min -- --watch'", "watch:js": "npm run build:js -- --watch" } -} +} \ No newline at end of file From df5b9ecc280fcfa3a08609cc8ac39f80320d9c33 Mon Sep 17 00:00:00 2001 From: koy Date: Sat, 20 Jul 2024 00:58:34 +0800 Subject: [PATCH 04/25] chore: fix ci --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66485e0f5..d89ee6268 100644 --- a/package.json +++ b/package.json @@ -118,4 +118,4 @@ "watch:css": "run-p 'build:css -- --watch' 'build:css:min -- --watch'", "watch:js": "npm run build:js -- --watch" } -} \ No newline at end of file +} From 31d3295549b213cb10cf2af109637ee99281b59d Mon Sep 17 00:00:00 2001 From: koy Date: Sat, 20 Jul 2024 01:40:49 +0800 Subject: [PATCH 05/25] chore: fix ci --- package.json | 6 ++-- test/e2e/configuration.test.js | 52 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index d89ee6268..91b865f23 100644 --- a/package.json +++ b/package.json @@ -108,9 +108,9 @@ "pub": "sh build/release.sh", "serve:dev": "npm run serve -- --dev", "serve": "node server", - "test:e2e": "npx playwright test", - "test:e2e:chromium": "npx playwright test --project='chromium'", - "test:e2e:ui": "npx playwright test --ui", + "test:e2e": "playwright test", + "test:e2e:chromium": "playwright test --project='chromium'", + "test:e2e:ui": "playwright test --ui", "test:integration": "npm run test:jest -- --selectProjects integration", "test:jest": "cross-env NODE_OPTIONS=--experimental-vm-modules jest", "test:unit": "npm run test:jest -- --selectProjects unit", diff --git a/test/e2e/configuration.test.js b/test/e2e/configuration.test.js index 04eae95fb..bae36bb51 100644 --- a/test/e2e/configuration.test.js +++ b/test/e2e/configuration.test.js @@ -38,32 +38,32 @@ test.describe('Configuration options', () => { await expect(mainElm).toContainText('beforeEach'); }); - test('false (throws uncaught errors)', async ({ page }) => { - let consoleMsg, errorMsg; - - page.on('console', msg => (consoleMsg = msg.text())); - page.on('pageerror', err => (errorMsg = err.message)); - - await docsifyInit({ - config: { - catchPluginErrors: false, - plugins: [ - function (hook, vm) { - hook.ready(function () { - fail(); - }); - }, - ], - }, - markdown: { - homepage: '# Hello World', - }, - // _logHTML: true, - }); - - expect(consoleMsg).toBeUndefined(); - expect(errorMsg).toContain('fail'); - }); + // test('false (throws uncaught errors)', async ({ page }) => { + // let consoleMsg, errorMsg; + + // page.on('console', msg => (consoleMsg = msg.text())); + // page.on('pageerror', err => (errorMsg = err.message)); + + // await docsifyInit({ + // config: { + // catchPluginErrors: false, + // plugins: [ + // function (hook, vm) { + // hook.ready(function () { + // fail(); + // }); + // }, + // ], + // }, + // markdown: { + // homepage: '# Hello World', + // }, + // // _logHTML: true, + // }); + + // expect(consoleMsg).toBeUndefined(); + // expect(errorMsg).toContain('fail'); + // }); }); test.describe('notFoundPage', () => { From 97b25f221acda7b22487ad64c904582eb178dac6 Mon Sep 17 00:00:00 2001 From: koy Date: Sat, 20 Jul 2024 16:45:54 +0800 Subject: [PATCH 06/25] chore: fix ci --- src/core/fetch/index.js | 1 + test/e2e/configuration.test.js | 62 +++++++++++++++++----------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js index bc14eeb4f..52b2ab1dd 100644 --- a/src/core/fetch/index.js +++ b/src/core/fetch/index.js @@ -75,6 +75,7 @@ export function Fetch(Base) { if (!loadSidebar) { // Although, we don't load sidebar from sidebar file, we still need call the render to auto generate sidebar from headings toc renderSidebar(); + return; } // Load sidebar from the sidebar file diff --git a/test/e2e/configuration.test.js b/test/e2e/configuration.test.js index bae36bb51..d081d95ed 100644 --- a/test/e2e/configuration.test.js +++ b/test/e2e/configuration.test.js @@ -5,10 +5,10 @@ import { test, expect } from './fixtures/docsify-init-fixture.js'; test.describe('Configuration options', () => { test.describe('catchPluginErrors', () => { test('true (handles uncaught errors)', async ({ page }) => { - // let consoleMsg, errorMsg; + let consoleMsg, errorMsg; - // page.on('console', msg => (consoleMsg = msg.text())); - // page.on('pageerror', err => (errorMsg = err.message)); + page.on('console', msg => (consoleMsg = msg.text())); + page.on('pageerror', err => (errorMsg = err.message)); await docsifyInit({ config: { @@ -32,38 +32,38 @@ test.describe('Configuration options', () => { const mainElm = page.locator('#main'); - // expect(errorMsg).toBeUndefined(); - // expect(consoleMsg).toContain('Docsify plugin error'); + expect(errorMsg).toBeUndefined(); + expect(consoleMsg).toContain('Docsify plugin error'); await expect(mainElm).toContainText('Hello World'); await expect(mainElm).toContainText('beforeEach'); }); - // test('false (throws uncaught errors)', async ({ page }) => { - // let consoleMsg, errorMsg; - - // page.on('console', msg => (consoleMsg = msg.text())); - // page.on('pageerror', err => (errorMsg = err.message)); - - // await docsifyInit({ - // config: { - // catchPluginErrors: false, - // plugins: [ - // function (hook, vm) { - // hook.ready(function () { - // fail(); - // }); - // }, - // ], - // }, - // markdown: { - // homepage: '# Hello World', - // }, - // // _logHTML: true, - // }); - - // expect(consoleMsg).toBeUndefined(); - // expect(errorMsg).toContain('fail'); - // }); + test('false (throws uncaught errors)', async ({ page }) => { + let consoleMsg, errorMsg; + + page.on('console', msg => (consoleMsg = msg.text())); + page.on('pageerror', err => (errorMsg = err.message)); + + await docsifyInit({ + config: { + catchPluginErrors: false, + plugins: [ + function (hook, vm) { + hook.ready(function () { + fail(); + }); + }, + ], + }, + markdown: { + homepage: '# Hello World', + }, + // _logHTML: true, + }); + + expect(consoleMsg).toBeUndefined(); + expect(errorMsg).toContain('fail'); + }); }); test.describe('notFoundPage', () => { From a942713600294dfcc8c948ae7000f018fee064d4 Mon Sep 17 00:00:00 2001 From: koy Date: Sat, 20 Jul 2024 17:00:34 +0800 Subject: [PATCH 07/25] remove sidebar call in renderMain --- src/core/render/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/render/index.js b/src/core/render/index.js index 774533c64..06f900bb7 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -84,9 +84,6 @@ export function Render(Base) { this._renderTo(markdownElm, html); - // Render sidebar with the TOC - !docsifyConfig.loadSidebar && this._renderSidebar(); - // Execute markdown