From 2b0003b9b07ea9f3622f9a7f5698650e9409179f Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Wed, 3 Apr 2024 17:25:10 +0800 Subject: [PATCH 01/14] Add rule based html validation --- packages/core/src/Page/index.ts | 2 + packages/core/src/utils/htmlValidationUtil.ts | 73 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 packages/core/src/utils/htmlValidationUtil.ts diff --git a/packages/core/src/Page/index.ts b/packages/core/src/Page/index.ts index 23fbf35b74..d88b19baf8 100644 --- a/packages/core/src/Page/index.ts +++ b/packages/core/src/Page/index.ts @@ -18,6 +18,7 @@ import type { SiteConfig } from '../Site/SiteConfig'; import type { FrontMatter } from '../plugins/Plugin'; import type { ExternalManager } from '../External/ExternalManager'; import { MbNode } from '../utils/node'; +import { validateHtmlWithRules } from '../utils/htmlValidationUtil'; import { LAYOUT_DEFAULT_NAME } from '../Layout'; @@ -549,6 +550,7 @@ export class Page { this.collectHeadingsAndKeywords(pageContent); content = `
${content}
`; + validateHtmlWithRules(content, this.pageConfig.sourcePath); // Compile the page into Vue application and outputs the render function into script for browser const compiledVuePage = await pageVueServerRenderer.compileVuePageAndCreateScript( diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts new file mode 100644 index 0000000000..20cf7c7586 --- /dev/null +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -0,0 +1,73 @@ +// eslint-disable-next-line max-classes-per-file +import * as cheerio from 'cheerio'; +import * as logger from './logger'; + +const rules: Rule[] = []; + +export function validateHtmlWithRules(content: string, path: string): void { + const $ = cheerio.load(content); + rules.forEach((rule) => { + rule.validateAndLog($, path); + }); +} + +class Rule { + description: string = ''; + causeHydration: boolean = false; + // eslint-disable-next-line lodash/prefer-constant + isValidDom: (rootNode: cheerio.Root) => boolean = () => true; + validateAndLog(rootNode: cheerio.Root, path: string) { + if (!this.isValidDom(rootNode)) { + if (this.causeHydration) { + logger.error(`Invalid HTML in ${path}. ${this.description}`); + } else { + logger.warn(`Invalid HTML in ${path}. ${this.description}`); + } + } + } +} + +class RuleBuilder { + rule: Rule = new Rule(); + + causeHydration(causeHydration: boolean = true) { + this.rule.causeHydration = causeHydration; + return this; + } + + description(description: string) { + this.rule.description = description; + return this; + } + + validator(Validator: (rootNode: cheerio.Root) => boolean) { + this.rule.isValidDom = Validator; + return this; + } + + pushRuleAndReset() { + rules.push(this.rule); + this.rule = new Rule(); + } + + reset() { + this.rule = new Rule(); + } +} + +const ruleBuilder = new RuleBuilder(); + +// To add a new rule, use the following template: +ruleBuilder.description('Table must have a tbody tag') + .causeHydration(true) + .validator((rootNode: cheerio.Root) => { + const tables = rootNode('table'); + for (let i = 0; i < tables.length; i += 1) { + const table = rootNode(tables[i]); + if (table.find('tbody').length === 0) { + return false; + } + } + return true; + }) + .pushRuleAndReset(); From 5f2d3d545f4ef4e53fbabcbf82e986efacdcae59 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 01:03:58 +0800 Subject: [PATCH 02/14] Rewrite --- docs/devGuide/design/serverSideRendering.md | 4 + packages/core/src/Page/index.ts | 4 +- packages/core/src/utils/htmlValidationUtil.ts | 77 +++-------------- .../unit/utils/HtmlValidationUtil.test.ts | 20 +++++ .../test/unit/utils/html_str/correct_html.txt | 86 +++++++++++++++++++ .../utils/html_str/table_without_tbody.txt | 53 ++++++++++++ 6 files changed, 176 insertions(+), 68 deletions(-) create mode 100644 packages/core/test/unit/utils/HtmlValidationUtil.test.ts create mode 100644 packages/core/test/unit/utils/html_str/correct_html.txt create mode 100644 packages/core/test/unit/utils/html_str/table_without_tbody.txt diff --git a/docs/devGuide/design/serverSideRendering.md b/docs/devGuide/design/serverSideRendering.md index fd1707e030..c2596ed350 100644 --- a/docs/devGuide/design/serverSideRendering.md +++ b/docs/devGuide/design/serverSideRendering.md @@ -102,6 +102,10 @@ Some common mistakes are as such: If you are unsure what elements are allowed within other elements, or what constitutes invalid HTML in general, a good resource to reference would be the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span). + +Modern browsers have robust inbuilt mechanism to auto-correct commons causes of hydration. Therefore, to prevent false positive, markbind only has a minimalist rule-based validation against violation. The code are in the `core/src/utils/htmlValidationUtils.ts`. Please help extend it when more violations that certainly cause hydration on browsers are spotted. + + Note that the list only included the common causes of hydration issue that MarkBind developers have ran into. There may be other causes of hydration issue that are not listed here (although unlikely). {% from "njk/common.njk" import previous_next %} diff --git a/packages/core/src/Page/index.ts b/packages/core/src/Page/index.ts index c7cf4259d7..78f8473256 100644 --- a/packages/core/src/Page/index.ts +++ b/packages/core/src/Page/index.ts @@ -18,7 +18,7 @@ import type { SiteConfig } from '../Site/SiteConfig'; import type { FrontMatter } from '../plugins/Plugin'; import type { ExternalManager } from '../External/ExternalManager'; import { MbNode } from '../utils/node'; -import { validateHtmlWithRules } from '../utils/htmlValidationUtil'; +import { checkForVueHydrationWithRules } from '../utils/htmlValidationUtil'; import { LAYOUT_DEFAULT_NAME } from '../Layout'; @@ -554,7 +554,7 @@ export class Page { this.collectHeadingsAndKeywords(pageContent); content = `
${content}
`; - validateHtmlWithRules(content, this.pageConfig.sourcePath); + checkForVueHydrationWithRules(content, this.pageConfig.sourcePath); // Compile the page into Vue application and outputs the render function into script for browser const compiledVuePage = await pageVueServerRenderer.compileVuePageAndCreateScript( diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 20cf7c7586..051782d973 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -1,73 +1,18 @@ -// eslint-disable-next-line max-classes-per-file -import * as cheerio from 'cheerio'; +import cheerio from 'cheerio'; import * as logger from './logger'; -const rules: Rule[] = []; - -export function validateHtmlWithRules(content: string, path: string): void { - const $ = cheerio.load(content); - rules.forEach((rule) => { - rule.validateAndLog($, path); - }); -} - -class Rule { - description: string = ''; - causeHydration: boolean = false; - // eslint-disable-next-line lodash/prefer-constant - isValidDom: (rootNode: cheerio.Root) => boolean = () => true; - validateAndLog(rootNode: cheerio.Root, path: string) { - if (!this.isValidDom(rootNode)) { - if (this.causeHydration) { - logger.error(`Invalid HTML in ${path}. ${this.description}`); - } else { - logger.warn(`Invalid HTML in ${path}. ${this.description}`); - } +function validateAndLogTableHasTbody(rootNode: cheerio.Root, path: string) { + const tables = rootNode('table'); + for (let i = 0; i < tables.length; i += 1) { + const table = rootNode(tables[i]); + if (table.find('tbody').length === 0) { + // eslint-disable-next-line max-len + logger.error(`Invalid HTML in ${path}. Table must have a tbody tag. Please correct this to avoid vue hydration issues.`); } } } -class RuleBuilder { - rule: Rule = new Rule(); - - causeHydration(causeHydration: boolean = true) { - this.rule.causeHydration = causeHydration; - return this; - } - - description(description: string) { - this.rule.description = description; - return this; - } - - validator(Validator: (rootNode: cheerio.Root) => boolean) { - this.rule.isValidDom = Validator; - return this; - } - - pushRuleAndReset() { - rules.push(this.rule); - this.rule = new Rule(); - } - - reset() { - this.rule = new Rule(); - } +export function checkForVueHydrationWithRules(content: string, path: string) { + const $ = cheerio.load(content); + validateAndLogTableHasTbody($, path); } - -const ruleBuilder = new RuleBuilder(); - -// To add a new rule, use the following template: -ruleBuilder.description('Table must have a tbody tag') - .causeHydration(true) - .validator((rootNode: cheerio.Root) => { - const tables = rootNode('table'); - for (let i = 0; i < tables.length; i += 1) { - const table = rootNode(tables[i]); - if (table.find('tbody').length === 0) { - return false; - } - } - return true; - }) - .pushRuleAndReset(); diff --git a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts new file mode 100644 index 0000000000..d844aa6059 --- /dev/null +++ b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts @@ -0,0 +1,20 @@ +import { readFileSync } from 'fs'; +import * as path from 'path'; +import * as logger from '../../../src/utils/logger'; +import { checkForVueHydrationWithRules } from '../../../src/utils/htmlValidationUtil'; + +const table_without_tbody = readFileSync(path.resolve(__dirname, 'html_str/table_without_tbody.txt'), 'utf8'); +const correct_html_string = readFileSync(path.resolve(__dirname, 'html_str/correct_html.txt'), 'utf8'); + +describe('checkForVueHydrationWithRules', () => { + it('should not log an error when the content is correct', () => { + const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); + checkForVueHydrationWithRules(correct_html_string, '/fake/path'); + expect(mockError).not.toHaveBeenCalled(); + }); + it('should log an error when a table does not have a tbody tag', () => { + const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); + checkForVueHydrationWithRules(table_without_tbody, '/fake/path'); + expect(mockError).toHaveBeenCalled(); + }); +}); diff --git a/packages/core/test/unit/utils/html_str/correct_html.txt b/packages/core/test/unit/utils/html_str/correct_html.txt new file mode 100644 index 0000000000..b7e1b1ee18 --- /dev/null +++ b/packages/core/test/unit/utils/html_str/correct_html.txt @@ -0,0 +1,86 @@ +
+
+ + +
  • Topic 1
  • +
  • Topic 2
  • + +
  • Topic 3a
  • +
  • Topic 3b
  • +
    +
    + + + +
    + + +
    +
    +
    +

    Great!
    You've just initialized a MarkBind site.

    +

    Let's get started...

    +
    +

    What just happened?

    +

    You have just initialized a default MarkBind site! It is equipped with a set of core features, including site and page navigation. Additionally, we have included some convenient links to our User Guide, to help you get started quickly and easily.

    + +

    If you were intending to convert an existing GitHub wiki or a docs folder into MarkBind, use the --convert flag instead. See User Guide: MarkBind in the Project Workflow for more information.

    +

    If you want to start with a minimal template instead, use the --template flag with the "minimal" option to initialize a minimal site instead of the default. See User Guide: Templates for more information.

    +
    + +

    This default site comes pre-configured with the core Navigation components: a siteNav, a pageNav, a NavBar, and a Search Bar. To help you get started with the siteNav, we have included five dummy placeholder pages. The NavBar also comes with a placeholder slot for your custom Logo.

    +
    +

    Guide to MarkBind

    +

    To see the capability of MarkBind in action, feel free to take a look at some of the websites built using MarkBind on our Showcase page.

    +

    For more information on how to work with MarkBind sites and to add content, refer to our comprehensive User Guide.

    + +

    If you are interested in contributing to MarkBind, you can refer to our Developer Guide as well!

    + +
    User Guide: Authoring Contents
    +
    +

    Learn about the variety of syntax schemes, formats, and custom MarkBind components that you can use in your MarkBind site.

    +

    More info in: User Guide → Authoring Contents

    +
    +
    User Guide: Working with Sites
    +
    +

    Learn how to modify site properties, apply themes, and enable/disable plugins for your MarkBind site.

    +

    More info in: User Guide → Working with Sites

    +
    +
    User Guide: Full Syntax Reference
    +
    +

    Refer to our Full Syntax Reference page to find a specific feature or component that you want to use in your MarkBind site.

    +

    More info in: User Guide → Full Syntax Reference

    +
    +
    + + +
    +
    \ No newline at end of file diff --git a/packages/core/test/unit/utils/html_str/table_without_tbody.txt b/packages/core/test/unit/utils/html_str/table_without_tbody.txt new file mode 100644 index 0000000000..0b9f454443 --- /dev/null +++ b/packages/core/test/unit/utils/html_str/table_without_tbody.txt @@ -0,0 +1,53 @@ +
    +
    + + +
  • Topic 1
  • +
  • Topic 2
  • + +
  • Topic 3a
  • +
  • Topic 3b
  • +
    +
    + + + +
    + + + + + + + + + + + +
    Task IDTaskEstimated EffortPrerequisite Task
    EPlanning for next version1 man dayD
    +
    + + +
    +
    \ No newline at end of file From 98ec18c05f0c2f8795f1ae6b1f964469152e3519 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 01:09:14 +0800 Subject: [PATCH 03/14] Improve docs --- docs/devGuide/design/serverSideRendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devGuide/design/serverSideRendering.md b/docs/devGuide/design/serverSideRendering.md index c2596ed350..b3693dbadd 100644 --- a/docs/devGuide/design/serverSideRendering.md +++ b/docs/devGuide/design/serverSideRendering.md @@ -103,7 +103,7 @@ If you are unsure what elements are allowed within other elements, or what const -Modern browsers have robust inbuilt mechanism to auto-correct commons causes of hydration. Therefore, to prevent false positive, markbind only has a minimalist rule-based validation against violation. The code are in the `core/src/utils/htmlValidationUtils.ts`. Please help extend it when more violations that certainly cause hydration on browsers are spotted. +Modern browsers have robust inbuilt mechanisms to auto-correct common causes of hydration. Therefore, to avoid high false positive rate, markbind only has a minimalist rule-based validation against violations. The code are in the `core/src/utils/htmlValidationUtils.ts`. Please help extend it when more violations that certainly cause hydration on browsers are spotted. Note that the list only included the common causes of hydration issue that MarkBind developers have ran into. There may be other causes of hydration issue that are not listed here (although unlikely). From e8cee88b47f0d8ef8ffe45b848ed166894c2fcad Mon Sep 17 00:00:00 2001 From: Wang Yiwen <121547057+yiwen101@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:57:35 +0800 Subject: [PATCH 04/14] Update docs/devGuide/design/serverSideRendering.md Co-authored-by: Chan Yu Cheng <77204346+yucheng11122017@users.noreply.github.com> --- docs/devGuide/design/serverSideRendering.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/devGuide/design/serverSideRendering.md b/docs/devGuide/design/serverSideRendering.md index b3693dbadd..bf25dba87a 100644 --- a/docs/devGuide/design/serverSideRendering.md +++ b/docs/devGuide/design/serverSideRendering.md @@ -103,7 +103,9 @@ If you are unsure what elements are allowed within other elements, or what const -Modern browsers have robust inbuilt mechanisms to auto-correct common causes of hydration. Therefore, to avoid high false positive rate, markbind only has a minimalist rule-based validation against violations. The code are in the `core/src/utils/htmlValidationUtils.ts`. Please help extend it when more violations that certainly cause hydration on browsers are spotted. +Modern browsers have robust inbuilt mechanisms to auto-correct common causes of hydration. Therefore, to avoid high false positive rate, Markbind has a minimalist rule-based validation against violations. The code for this is located in `core/src/utils/htmlValidationUtils.ts`. + +Please help extend it when violations that cause hydration on browsers are spotted. Note that the list only included the common causes of hydration issue that MarkBind developers have ran into. There may be other causes of hydration issue that are not listed here (although unlikely). From 4630d354744b8bcca2952ea900c20f3ca419d06c Mon Sep 17 00:00:00 2001 From: Wang Yiwen <121547057+yiwen101@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:59:00 +0800 Subject: [PATCH 05/14] Update packages/core/test/unit/utils/HtmlValidationUtil.test.ts Co-authored-by: Chan Yu Cheng <77204346+yucheng11122017@users.noreply.github.com> --- packages/core/test/unit/utils/HtmlValidationUtil.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts index d844aa6059..8914d965f6 100644 --- a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts +++ b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts @@ -12,7 +12,7 @@ describe('checkForVueHydrationWithRules', () => { checkForVueHydrationWithRules(correct_html_string, '/fake/path'); expect(mockError).not.toHaveBeenCalled(); }); - it('should log an error when a table does not have a tbody tag', () => { + it(' should log an error when a table does not have a tbody tag', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); checkForVueHydrationWithRules(table_without_tbody, '/fake/path'); expect(mockError).toHaveBeenCalled(); From 186155c0c2357eeb41e5ca3616fada2c63cf128e Mon Sep 17 00:00:00 2001 From: Wang Yiwen <121547057+yiwen101@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:13:01 +0800 Subject: [PATCH 06/14] Update packages/core/src/utils/htmlValidationUtil.ts Co-authored-by: Chan Yu Cheng <77204346+yucheng11122017@users.noreply.github.com> --- packages/core/src/utils/htmlValidationUtil.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 051782d973..63c1bdac45 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -7,7 +7,7 @@ function validateAndLogTableHasTbody(rootNode: cheerio.Root, path: string) { const table = rootNode(tables[i]); if (table.find('tbody').length === 0) { // eslint-disable-next-line max-len - logger.error(`Invalid HTML in ${path}. Table must have a tbody tag. Please correct this to avoid vue hydration issues.`); + logger.error(`Invalid HTML in ${path}:\n Table must have a tbody tag. Please correct this to avoid vue hydration issues.`); } } } From 6c7ede82b2bda0847eb42088faff824737e23076 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 12:31:03 +0800 Subject: [PATCH 07/14] Improve code quality --- packages/core/src/Page/index.ts | 4 +- packages/core/src/utils/htmlValidationUtil.ts | 9 ++-- .../unit/utils/HtmlValidationUtil.test.ts | 25 ++++++--- .../correctHtml.txt} | 0 .../unit/utils/htmlStr/tableWithTbody.txt | 54 +++++++++++++++++++ .../tableWithoutTbody.txt} | 0 6 files changed, 79 insertions(+), 13 deletions(-) rename packages/core/test/unit/utils/{html_str/correct_html.txt => htmlStr/correctHtml.txt} (100%) create mode 100644 packages/core/test/unit/utils/htmlStr/tableWithTbody.txt rename packages/core/test/unit/utils/{html_str/table_without_tbody.txt => htmlStr/tableWithoutTbody.txt} (100%) diff --git a/packages/core/src/Page/index.ts b/packages/core/src/Page/index.ts index 78f8473256..58256a3838 100644 --- a/packages/core/src/Page/index.ts +++ b/packages/core/src/Page/index.ts @@ -18,7 +18,7 @@ import type { SiteConfig } from '../Site/SiteConfig'; import type { FrontMatter } from '../plugins/Plugin'; import type { ExternalManager } from '../External/ExternalManager'; import { MbNode } from '../utils/node'; -import { checkForVueHydrationWithRules } from '../utils/htmlValidationUtil'; +import { checkForVueHydrationViolation } from '../utils/htmlValidationUtil'; import { LAYOUT_DEFAULT_NAME } from '../Layout'; @@ -554,7 +554,7 @@ export class Page { this.collectHeadingsAndKeywords(pageContent); content = `
    ${content}
    `; - checkForVueHydrationWithRules(content, this.pageConfig.sourcePath); + checkForVueHydrationViolation(content, this.pageConfig.sourcePath); // Compile the page into Vue application and outputs the render function into script for browser const compiledVuePage = await pageVueServerRenderer.compileVuePageAndCreateScript( diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 63c1bdac45..d7f3cb9567 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -1,18 +1,19 @@ import cheerio from 'cheerio'; import * as logger from './logger'; -function validateAndLogTableHasTbody(rootNode: cheerio.Root, path: string) { +function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { const tables = rootNode('table'); for (let i = 0; i < tables.length; i += 1) { const table = rootNode(tables[i]); if (table.find('tbody').length === 0) { // eslint-disable-next-line max-len - logger.error(`Invalid HTML in ${path}:\n Table must have a tbody tag. Please correct this to avoid vue hydration issues.`); + logger.error(`Invalid HTML in ${path}.\n` + + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.'); } } } -export function checkForVueHydrationWithRules(content: string, path: string) { +export function checkForVueHydrationViolation(content: string, path: string) { const $ = cheerio.load(content); - validateAndLogTableHasTbody($, path); + logWarningForMissingTbody($, path); } diff --git a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts index 8914d965f6..86b751a272 100644 --- a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts +++ b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts @@ -1,20 +1,31 @@ import { readFileSync } from 'fs'; import * as path from 'path'; import * as logger from '../../../src/utils/logger'; -import { checkForVueHydrationWithRules } from '../../../src/utils/htmlValidationUtil'; +import { checkForVueHydrationViolation } from '../../../src/utils/htmlValidationUtil'; -const table_without_tbody = readFileSync(path.resolve(__dirname, 'html_str/table_without_tbody.txt'), 'utf8'); -const correct_html_string = readFileSync(path.resolve(__dirname, 'html_str/correct_html.txt'), 'utf8'); +const table_without_tbody = readFileSync(path.resolve(__dirname, 'htmlStr/tableWithoutTbody.txt'), 'utf8'); +const table_with_tbody = readFileSync(path.resolve(__dirname, 'htmlStr/tableWithTbody.txt'), 'utf8'); +const correct_html_string = readFileSync(path.resolve(__dirname, 'htmlStr/correctHtml.txt'), 'utf8'); -describe('checkForVueHydrationWithRules', () => { - it('should not log an error when the content is correct', () => { +describe('checkForVueHydrationViolation', () => { + it(' should not log an error when the content is correct', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); - checkForVueHydrationWithRules(correct_html_string, '/fake/path'); + checkForVueHydrationViolation(correct_html_string, '/fake/path'); expect(mockError).not.toHaveBeenCalled(); }); + + it(' should not log an error when a table does not have a tbody tag', () => { + const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); + checkForVueHydrationViolation(table_with_tbody, '/fake/path'); + expect(mockError).not.toHaveBeenCalled(); + }); + it(' should log an error when a table does not have a tbody tag', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); - checkForVueHydrationWithRules(table_without_tbody, '/fake/path'); + checkForVueHydrationViolation(table_without_tbody, '/fake/path'); expect(mockError).toHaveBeenCalled(); + const expectedMessange = 'Invalid HTML in /fake/path.\n' + + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.'; + expect(mockError).toHaveBeenCalledWith(expectedMessange); }); }); diff --git a/packages/core/test/unit/utils/html_str/correct_html.txt b/packages/core/test/unit/utils/htmlStr/correctHtml.txt similarity index 100% rename from packages/core/test/unit/utils/html_str/correct_html.txt rename to packages/core/test/unit/utils/htmlStr/correctHtml.txt diff --git a/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt b/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt new file mode 100644 index 0000000000..02b4bf84bf --- /dev/null +++ b/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt @@ -0,0 +1,54 @@ +
    +
    + + +
  • Topic 1
  • +
  • Topic 2
  • + +
  • Topic 3a
  • +
  • Topic 3b
  • +
    +
    + + + +
    + + + + + + + + + + + + +
    Task IDTaskEstimated EffortPrerequisite Task
    EPlanning for next version1 man dayD
    +
    + + +
    +
    \ No newline at end of file diff --git a/packages/core/test/unit/utils/html_str/table_without_tbody.txt b/packages/core/test/unit/utils/htmlStr/tableWithoutTbody.txt similarity index 100% rename from packages/core/test/unit/utils/html_str/table_without_tbody.txt rename to packages/core/test/unit/utils/htmlStr/tableWithoutTbody.txt From 843316ca11018a926217c4499a216458d62125b7 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 12:36:32 +0800 Subject: [PATCH 08/14] Add newline after the log --- packages/core/src/utils/htmlValidationUtil.ts | 2 +- packages/core/test/unit/utils/HtmlValidationUtil.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index d7f3cb9567..247942e2c0 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -8,7 +8,7 @@ function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { if (table.find('tbody').length === 0) { // eslint-disable-next-line max-len logger.error(`Invalid HTML in ${path}.\n` - + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.'); + + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.\n'); } } } diff --git a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts index 86b751a272..78b3563f58 100644 --- a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts +++ b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts @@ -25,7 +25,7 @@ describe('checkForVueHydrationViolation', () => { checkForVueHydrationViolation(table_without_tbody, '/fake/path'); expect(mockError).toHaveBeenCalled(); const expectedMessange = 'Invalid HTML in /fake/path.\n' - + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.'; + + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.\n'; expect(mockError).toHaveBeenCalledWith(expectedMessange); }); }); From be965f232b1405e19230a770e487f7ea61438f03 Mon Sep 17 00:00:00 2001 From: Wang Yiwen <121547057+yiwen101@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:11:25 +0800 Subject: [PATCH 09/14] Update packages/core/test/unit/utils/HtmlValidationUtil.test.ts Co-authored-by: Chan Yu Cheng <77204346+yucheng11122017@users.noreply.github.com> --- packages/core/test/unit/utils/HtmlValidationUtil.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts index 78b3563f58..8ab1c542a9 100644 --- a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts +++ b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts @@ -14,7 +14,7 @@ describe('checkForVueHydrationViolation', () => { expect(mockError).not.toHaveBeenCalled(); }); - it(' should not log an error when a table does not have a tbody tag', () => { + it(' should not log an error when all tables have a tbody tag', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); checkForVueHydrationViolation(table_with_tbody, '/fake/path'); expect(mockError).not.toHaveBeenCalled(); From 69a1e3a80fe486bc267307908220c56556453e82 Mon Sep 17 00:00:00 2001 From: Wang Yiwen <121547057+yiwen101@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:15:13 +0800 Subject: [PATCH 10/14] Update packages/core/src/utils/htmlValidationUtil.ts Co-authored-by: Chan Yu Cheng <77204346+yucheng11122017@users.noreply.github.com> --- packages/core/src/utils/htmlValidationUtil.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 247942e2c0..0528707be8 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -8,7 +8,7 @@ function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { if (table.find('tbody').length === 0) { // eslint-disable-next-line max-len logger.error(`Invalid HTML in ${path}.\n` - + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.\n'); + + 'Table must have a tbody tag. Please correct this to avoid Vue hydration issues.\n'); } } } From 3df4777a62ddabb3f348a451fe6101c927ce0e41 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 15:22:42 +0800 Subject: [PATCH 11/14] Update test --- packages/core/src/utils/htmlValidationUtil.ts | 1 - .../test/unit/utils/HtmlValidationUtil.test.ts | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 0528707be8..9005cab951 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -6,7 +6,6 @@ function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { for (let i = 0; i < tables.length; i += 1) { const table = rootNode(tables[i]); if (table.find('tbody').length === 0) { - // eslint-disable-next-line max-len logger.error(`Invalid HTML in ${path}.\n` + 'Table must have a tbody tag. Please correct this to avoid Vue hydration issues.\n'); } diff --git a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts index 8ab1c542a9..471684fa10 100644 --- a/packages/core/test/unit/utils/HtmlValidationUtil.test.ts +++ b/packages/core/test/unit/utils/HtmlValidationUtil.test.ts @@ -3,29 +3,29 @@ import * as path from 'path'; import * as logger from '../../../src/utils/logger'; import { checkForVueHydrationViolation } from '../../../src/utils/htmlValidationUtil'; -const table_without_tbody = readFileSync(path.resolve(__dirname, 'htmlStr/tableWithoutTbody.txt'), 'utf8'); -const table_with_tbody = readFileSync(path.resolve(__dirname, 'htmlStr/tableWithTbody.txt'), 'utf8'); -const correct_html_string = readFileSync(path.resolve(__dirname, 'htmlStr/correctHtml.txt'), 'utf8'); +const tableWithoutTbody = readFileSync(path.resolve(__dirname, 'htmlStr/tableWithoutTbody.txt'), 'utf8'); +const tableWithTbody = readFileSync(path.resolve(__dirname, 'htmlStr/tableWithTbody.txt'), 'utf8'); +const correctHtmlString = readFileSync(path.resolve(__dirname, 'htmlStr/correctHtml.txt'), 'utf8'); describe('checkForVueHydrationViolation', () => { it(' should not log an error when the content is correct', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); - checkForVueHydrationViolation(correct_html_string, '/fake/path'); + checkForVueHydrationViolation(correctHtmlString, '/fake/path'); expect(mockError).not.toHaveBeenCalled(); }); it(' should not log an error when all tables have a tbody tag', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); - checkForVueHydrationViolation(table_with_tbody, '/fake/path'); + checkForVueHydrationViolation(tableWithTbody, '/fake/path'); expect(mockError).not.toHaveBeenCalled(); }); it(' should log an error when a table does not have a tbody tag', () => { const mockError = jest.spyOn(logger, 'error').mockImplementation(() => {}); - checkForVueHydrationViolation(table_without_tbody, '/fake/path'); + checkForVueHydrationViolation(tableWithoutTbody, '/fake/path'); expect(mockError).toHaveBeenCalled(); const expectedMessange = 'Invalid HTML in /fake/path.\n' - + 'Table must have a tbody tag. Please correct this to avoid vue hydration issues.\n'; + + 'Table must have a tbody tag. Please correct this to avoid Vue hydration issues.\n'; expect(mockError).toHaveBeenCalledWith(expectedMessange); }); }); From 9375e983ec5d85736e098dc29daec704f2e0afb7 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 17:05:14 +0800 Subject: [PATCH 12/14] Update testcase --- .../unit/utils/htmlStr/tableWithTbody.txt | 71 +++++-------------- .../unit/utils/htmlStr/tableWithoutTbody.txt | 68 ++++-------------- 2 files changed, 32 insertions(+), 107 deletions(-) diff --git a/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt b/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt index 02b4bf84bf..011cbfc9df 100644 --- a/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt +++ b/packages/core/test/unit/utils/htmlStr/tableWithTbody.txt @@ -1,54 +1,17 @@ -
    -
    - - -
  • Topic 1
  • -
  • Topic 2
  • - -
  • Topic 3a
  • -
  • Topic 3b
  • -
    -
    - - - -
    - - - - - - - - - - - - -
    Task IDTaskEstimated EffortPrerequisite Task
    EPlanning for next version1 man dayD
    -
    - - -
    -
    \ No newline at end of file +
    + + + + + + + + + + + + + + +
    Task IDTaskEstimated EffortPrerequisite Task
    EPlanning for next version1 man dayD
    +
    \ No newline at end of file diff --git a/packages/core/test/unit/utils/htmlStr/tableWithoutTbody.txt b/packages/core/test/unit/utils/htmlStr/tableWithoutTbody.txt index 0b9f454443..ef8b354f69 100644 --- a/packages/core/test/unit/utils/htmlStr/tableWithoutTbody.txt +++ b/packages/core/test/unit/utils/htmlStr/tableWithoutTbody.txt @@ -1,53 +1,15 @@ -
    -
    - - -
  • Topic 1
  • -
  • Topic 2
  • - -
  • Topic 3a
  • -
  • Topic 3b
  • -
    -
    - - - -
    - - - - - - - - - - - -
    Task IDTaskEstimated EffortPrerequisite Task
    EPlanning for next version1 man dayD
    -
    - - -
    -
    \ No newline at end of file +
    + + + + + + + + + + + + +
    Task IDTaskEstimated EffortPrerequisite Task
    EPlanning for next version1 man dayD
    +
    \ No newline at end of file From c052a4d3eaca82285213a1957ca88bc549c5a093 Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 17:15:47 +0800 Subject: [PATCH 13/14] Improve code quality --- packages/core/src/utils/htmlValidationUtil.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 9005cab951..8c8420818f 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -1,6 +1,11 @@ import cheerio from 'cheerio'; import * as logger from './logger'; +export function checkForVueHydrationViolation(content: string, path: string) { + const $ = cheerio.load(content); + logWarningForMissingTbody($, path); +} + function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { const tables = rootNode('table'); for (let i = 0; i < tables.length; i += 1) { @@ -11,8 +16,3 @@ function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { } } } - -export function checkForVueHydrationViolation(content: string, path: string) { - const $ = cheerio.load(content); - logWarningForMissingTbody($, path); -} From ff5634c095d2619731ab11551ea813bc9702d0fb Mon Sep 17 00:00:00 2001 From: Wang Yiwen Date: Thu, 18 Apr 2024 17:18:08 +0800 Subject: [PATCH 14/14] Revert "Improve code quality" This reverts commit c052a4d3eaca82285213a1957ca88bc549c5a093. --- packages/core/src/utils/htmlValidationUtil.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/utils/htmlValidationUtil.ts b/packages/core/src/utils/htmlValidationUtil.ts index 8c8420818f..9005cab951 100644 --- a/packages/core/src/utils/htmlValidationUtil.ts +++ b/packages/core/src/utils/htmlValidationUtil.ts @@ -1,11 +1,6 @@ import cheerio from 'cheerio'; import * as logger from './logger'; -export function checkForVueHydrationViolation(content: string, path: string) { - const $ = cheerio.load(content); - logWarningForMissingTbody($, path); -} - function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { const tables = rootNode('table'); for (let i = 0; i < tables.length; i += 1) { @@ -16,3 +11,8 @@ function logWarningForMissingTbody(rootNode: cheerio.Root, path: string) { } } } + +export function checkForVueHydrationViolation(content: string, path: string) { + const $ = cheerio.load(content); + logWarningForMissingTbody($, path); +}