From 4a819716059ccf3848e5921f17dfa8f6679de511 Mon Sep 17 00:00:00 2001 From: Sebastian Romero Date: Mon, 26 Apr 2021 13:54:30 +0200 Subject: [PATCH 1/2] Add check for broken image links --- scripts/validation/validate.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/validation/validate.js b/scripts/validation/validate.js index 3fb3ef94..7c1e6649 100644 --- a/scripts/validation/validate.js +++ b/scripts/validation/validate.js @@ -169,7 +169,7 @@ validator.addValidation(async (tutorials) => { /** - * Verify that the images don't have an absolute path + * Verify that the images exist and don't have an absolute path */ validator.addValidation(async (tutorials) => { let errorsOccurred = []; @@ -181,6 +181,12 @@ validator.addValidation(async (tutorials) => { const lineNumber = fileHelper.getLineNumberFromIndex(content.indexOf(imagePath), content); errorsOccurred.push(new ValidationError(errorMessage, tutorial.path, lineNumber)); } + if(!fs.existsSync(imagePath)){ + const errorMessage = "Image doesn't exist: " + imagePath; + const content = tutorial.markdown; + const lineNumber = fileHelper.getLineNumberFromIndex(content.indexOf(imagePath), content); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.path, lineNumber)); + } }); }); return errorsOccurred; From 970873ffe8c3543b7bc085d36a19f2c7507762e1 Mon Sep 17 00:00:00 2001 From: Sebastian Romero Date: Mon, 26 Apr 2021 16:30:13 +0200 Subject: [PATCH 2/2] Check for broken image paths --- scripts/validation/domain/tutorial.js | 14 +++++++---- scripts/validation/validate.js | 35 +++++++++++++-------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/scripts/validation/domain/tutorial.js b/scripts/validation/domain/tutorial.js index db90deaa..c9765326 100644 --- a/scripts/validation/domain/tutorial.js +++ b/scripts/validation/domain/tutorial.js @@ -13,15 +13,19 @@ var Tutorial = class Tutorial { } get path(){ + return this.basePath; + } + + get contentFilePath(){ return this.basePath + "/content.md"; } get markdown(){ - if(!fs.existsSync(this.path)){ - console.log("❌ File doens't exist " + this.path); + if(!fs.existsSync(this.contentFilePath)){ + console.log("❌ File doens't exist " + this.contentFilePath); return null; } - let rawData = fs.readFileSync(this.path).toString(); + let rawData = fs.readFileSync(this.contentFilePath).toString(); const content = fm(rawData); return content.body; } @@ -80,11 +84,11 @@ var Tutorial = class Tutorial { get metadata(){ try { - let rawData = fs.readFileSync(this.path).toString(); + let rawData = fs.readFileSync(this.contentFilePath).toString(); const content = fm(rawData); return content.attributes; } catch (error) { - console.log("Error occurred while parsing " + this.path); + console.log("Error occurred while parsing " + this.contentFilePath); console.log(error); return null; } diff --git a/scripts/validation/validate.js b/scripts/validation/validate.js index 7c1e6649..11c8f10e 100644 --- a/scripts/validation/validate.js +++ b/scripts/validation/validate.js @@ -33,29 +33,29 @@ validator.addValidation(async (tutorials) => { let jsonData = tutorial.metadata; if(!jsonData) { const errorMessage = "No metadata found"; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); return; } try { if(!jsonData.coverImage){ const errorMessage = "No cover image found"; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } else if (jsonData.coverImage.indexOf(".svg") == -1) { const errorMessage = "Cover image is not in SVG format."; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } let jsonSchema = JSON.parse(fs.readFileSync(config.metadataSchema)); let validationResult = validate(jsonData, jsonSchema); if(validationResult.errors.length != 0){ const errorMessage = `An error occurred while validating the metadata ${validationResult}`; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } } catch (error) { const errorMessage = "An error occurred while parsing the metadata"; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } }); return errorsOccurred; @@ -70,11 +70,11 @@ validator.addValidation(async (tutorials) => { tutorial.headings.forEach(heading => { if(tc.titleCase(heading) != heading){ const errorMessage = heading + "' is not title case"; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } if(heading.length > config.headingMaxLength){ const errorMessage = heading + "' (" + heading.length + ") exceeds the max length (" + config.headingMaxLength + ")"; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } }); }); @@ -97,7 +97,7 @@ validator.addValidation(async (tutorials) => { // Detect if there are embedded images that are actually rendered if(image.attributes.width || image.attributes.height){ const errorMessage = path + " contains embedded binary images."; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path, "warning")); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath, "warning")); } } }); @@ -131,7 +131,7 @@ validator.addValidation(async (tutorials) => { console.log('👍 %s is alive', result.link); } else if(result.status == "dead" && result.statusCode !== 0){ const errorMessage = `${result.link} is dead 💀 HTTP ${result.statusCode}`; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } }); resolve(errorsOccurred); @@ -159,7 +159,7 @@ validator.addValidation(async (tutorials) => { assetNames.forEach(asset => { if(coverImageName == asset) return; if(!imageNames.includes(asset) && !linkNames.includes(asset)){ - const errorMessage = asset + " is not used"; + const errorMessage = asset + " is not used."; errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); } }); @@ -179,13 +179,12 @@ validator.addValidation(async (tutorials) => { const errorMessage = "Image uses an absolute path: " + imagePath; const content = tutorial.markdown; const lineNumber = fileHelper.getLineNumberFromIndex(content.indexOf(imagePath), content); - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path, lineNumber)); - } - if(!fs.existsSync(imagePath)){ + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath, "error", lineNumber)); + } else if(!imagePath.startsWith("http") && !fs.existsSync(`${tutorial.path}/${imagePath}`)){ const errorMessage = "Image doesn't exist: " + imagePath; const content = tutorial.markdown; const lineNumber = fileHelper.getLineNumberFromIndex(content.indexOf(imagePath), content); - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path, lineNumber)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath, "error", lineNumber)); } }); }); @@ -203,7 +202,7 @@ validator.addValidation(async (tutorials) => { let nodes = tutorial.html.querySelectorAll("li ul"); if(nodes && nodes.length > 0){ const errorMessage = "Content uses nested lists"; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } }); return errorsOccurred; @@ -220,7 +219,7 @@ validator.addValidation(async (tutorials) => { const imageDescription = image.attributes.alt; if(imageDescription.split(" ").length <= 1){ const errorMessage = "Image doesn't have a description: " + image.attributes.src; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } }); }); @@ -239,7 +238,7 @@ validator.addValidation(async (tutorials) => { if(syntax) syntax = syntax.replace(PARSER_SYNTAX_PREFIX, ''); if(!config.allowedSyntaxSpecifiers.includes(syntax)){ const errorMessage = "Code block uses unsupported syntax: " + syntax; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath)); } }); }); @@ -280,7 +279,7 @@ validator.addValidation(async (tutorials) => { } if((match === null && rule.shouldMatch) || (match !== null && !rule.shouldMatch)) { const errorMessage = rule.errorMessage; - errorsOccurred.push(new ValidationError(errorMessage, tutorial.path, ruleType, lineNumber)); + errorsOccurred.push(new ValidationError(errorMessage, tutorial.contentFilePath, ruleType, lineNumber)); } }