From 836e217a3928db13643cdb0ec0cdbc896ed78f59 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Fri, 21 Sep 2018 17:06:11 +0200 Subject: [PATCH] Add suggestions and tooltips in broken link safety net --- .../src/__tests__/BrokenLinkSafetyNet.test.js | 156 +++++++++++------- 1 file changed, 96 insertions(+), 60 deletions(-) diff --git a/server/sonar-docs/src/__tests__/BrokenLinkSafetyNet.test.js b/server/sonar-docs/src/__tests__/BrokenLinkSafetyNet.test.js index 5eafbfa7178..91a47bc91a4 100644 --- a/server/sonar-docs/src/__tests__/BrokenLinkSafetyNet.test.js +++ b/server/sonar-docs/src/__tests__/BrokenLinkSafetyNet.test.js @@ -23,118 +23,154 @@ const path = require('path'); const glob = require('glob-promise'); const visit = require('unist-util-visit'); -it('should not have any broken link', async () => { - const root = path.resolve(__dirname + '/..'); - const files = await glob(root + '/pages/**/*.md') - .then(files => files.map(file => file.substr(root.length + 1))) - .then(files => - files.map(file => ({ - path: file.slice(0, -3), - content: handleIncludes(fs.readFileSync(root + '/' + file, 'utf8'), root) - })) - ); +const rootPath = path.resolve(path.join(__dirname, '/..')); +let files, parsedFiles; - const parsedFiles = files.map(file => { +beforeAll(async () => { + files = await loadGlobFiles('/pages/**/*.md'); + parsedFiles = files.map(file => { return { ...separateFrontMatter(file.content), path: file.path }; }); +}); +it('should have valid links in trees files', () => { const trees = [ 'SonarCloudNavigationTree.json', 'SonarQubeNavigationTree.json', 'StaticNavigationTree.json' ]; + let hasErrors = false; trees.forEach(file => { - const tree = JSON.parse(fs.readFileSync(root + '/../static/' + file, 'utf8')); + const tree = JSON.parse(fs.readFileSync(path.join(rootPath, '..', 'static', file), 'utf8')); tree.forEach(leaf => { if (typeof leaf === 'object') { if (leaf.children) { leaf.children.forEach(child => { // Check children markdown file path validity - const result = urlExists(parsedFiles, child); - if (!result) { - // Display custom error message - console.log('[', child, '] is not a valid link, in ', file); + if (!urlExists(parsedFiles, child)) { + console.log('[', child, '] is not a valid link, in', file); + hasErrors = true; } - expect(result).toBeTruthy(); }); } } else { // Check markdown file path validity - const result = urlExists(parsedFiles, leaf); - if (!result) { - console.log('[', leaf, '] is not a valid link, in ', file); + if (!urlExists(parsedFiles, leaf)) { + console.log('[', leaf, '] is not a valid link, in', file); + hasErrors = true; } - expect(result).toBeTruthy(); } }); }); + expect(hasErrors).toBeFalsy(); +}); + +it('should have valid links in suggestions file', () => { + const file = 'EmbedDocsSuggestions.json'; + const suggestions = JSON.parse(fs.readFileSync(path.join(rootPath, file), 'utf8')); + let hasErrors = false; + Object.keys(suggestions).forEach(key => { + suggestions[key].forEach(suggestion => { + if (!suggestion.link.startsWith('/documentation/')) { + console.log('[', suggestion.link, '] should starts with "/documentation/", in', file); + hasErrors = true; + } else if (!urlExists(parsedFiles, suggestion.link.replace('/documentation', ''))) { + console.log('[', suggestion.link, '] is not a valid link, in', file); + hasErrors = true; + } + }); + }); + expect(hasErrors).toBeFalsy(); +}); - // Check if all url tag in frontmatter are valid and uniques +it('should have valid and uniq links in url metadata field', () => { let urlLists = []; - parsedFiles.map(file => { - let result = file.frontmatter.url; - if (!result) { + let hasErrors = false; + parsedFiles.forEach(file => { + if (!file.frontmatter.url) { console.log('[', file.path, '] has no url metadata'); + hasErrors = true; + } else if (!checkUrlFormat(file.frontmatter.url, file.path)) { + hasErrors = true; + } else if (urlLists.includes(file.frontmatter.url)) { + console.log('[', file.path, '] has an url that is not unique', file.frontmatter.url); + hasErrors = true; } - expect(result).toBeTruthy(); - result = file.frontmatter.url.startsWith('/'); - if (!result) { - console.log('[', file.path, '] should starts with a slash ', file.frontmatter.url); - } - expect(result).toBeTruthy(); + urlLists = [...urlLists, file.frontmatter.url]; + }); + expect(hasErrors).toBeFalsy(); +}); - result = file.frontmatter.url.endsWith('/'); - if (!result) { - console.log('[', file.path, '] should ends with a slash ', file.frontmatter.url); - } - expect(result).toBeTruthy(); +it('should have valid links pointing to documentation inside pages', () => { + checkContentUrl(parsedFiles); +}); - result = !urlLists.includes(file.frontmatter.url); - if (!result) { - console.log('[', file.path, '] has an url that is not unique ', file.frontmatter.url); - } - expect(result).toBeTruthy(); +it('should have valid links inside tooltips', async () => { + const files = await loadGlobFiles('/tooltips/**/*.md'); + checkContentUrl(files); +}); - urlLists = [...urlLists, file.frontmatter.url]; +function handleIncludes(content, rootPath) { + return content.replace(/@include (.+)/, (match, p) => { + const filePath = path.join(rootPath, '..', `${p}.md`); + return fs.readFileSync(filePath, 'utf8'); }); +} - parsedFiles.map(file => { - const ast = remark().parse(file.content); - visit(ast, node => { +function checkContentUrl(files) { + let hasErrors = false; + files.forEach(file => { + visit(remark().parse(file.content), node => { if (node.type === 'image' && !node.url.startsWith('http')) { // Check image path validity - const result = fs.existsSync(root + '/' + node.url); - if (!result) { - console.log('[', node.url, '] is not a valid image path, in ', file.path + '.md'); + if (!fs.existsSync(path.join(rootPath, node.url))) { + console.log('[', node.url, '] is not a valid image path, in', file.path + '.md'); + hasErrors = true; } - expect(result).toBeTruthy(); } else if ( node.type === 'link' && !node.url.startsWith('http') && !node.url.startsWith('/#') ) { // Check markdown file path validity - const result = urlExists(parsedFiles, node.url); - if (!result) { - console.log('[', node.url, '] is not a valid link, in ', file.path + '.md'); + if (!urlExists(parsedFiles, node.url)) { + console.log('[', node.url, '] is not a valid link, in', file.path + '.md'); + hasErrors = true; } - expect(result).toBeTruthy(); } }); }); - expect(true).toBeTruthy(); -}); + expect(hasErrors).toBeFalsy(); +} function urlExists(files, url) { return files.find(f => f.frontmatter.url === url) !== undefined; } -function handleIncludes(content, root) { - return content.replace(/@include (.+)/, (match, p) => { - const filePath = path.join(root, '..', `${p}.md`); - return fs.readFileSync(filePath, 'utf8'); - }); +function checkUrlFormat(url, file) { + const noError = true; + + if (!url.startsWith('/')) { + console.log('[', file, '] should starts with a slash', url); + noError = false; + } + if (!url.endsWith('/')) { + console.log('[', file, '] should ends with a slash', url); + noError = false; + } + return noError; +} + +function loadGlobFiles(globPath) { + return glob(path.join(rootPath, globPath)) + .then(files => files.map(file => file.substr(rootPath.length + 1))) + .then(files => + files.map(file => ({ + path: file.slice(0, -3), + content: handleIncludes(fs.readFileSync(path.join(rootPath, file), 'utf8'), rootPath) + })) + ); } function getFrontMatterPosition(lines) { -- 2.39.5