]> source.dussan.org Git - sonarqube.git/commitdiff
Add suggestions and tooltips in broken link safety net
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Fri, 21 Sep 2018 15:06:11 +0000 (17:06 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 24 Sep 2018 18:20:59 +0000 (20:20 +0200)
server/sonar-docs/src/__tests__/BrokenLinkSafetyNet.test.js

index 5eafbfa7178f85c3172a20728b6ed9ac7ab48645..91a47bc91a439e4362cd3fdeef2675d0894539d4 100644 (file)
@@ -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) {