Skip to content

Commit 616cae4

Browse files
authored
Merge pull request #1 from arduino/article-naming
Validate article folder names
2 parents a056e02 + 174624d commit 616cae4

File tree

9 files changed

+237
-3
lines changed

9 files changed

+237
-3
lines changed

.github/workflows/jest.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: Jest
2+
on: push
3+
jobs:
4+
test:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v2
8+
- name: Setup Node.js
9+
uses: actions/setup-node@v1
10+
with:
11+
node-version: "14"
12+
13+
# Speed up subsequent runs with caching
14+
- name: Cache node modules
15+
uses: actions/cache@v2
16+
env:
17+
cache-name: cache-node-modules
18+
with:
19+
# npm cache files are stored in `~/.npm` on Linux/macOS
20+
path: ~/.npm
21+
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
22+
restore-keys: |
23+
${{ runner.os }}-build-${{ env.cache-name }}-
24+
${{ runner.os }}-build-
25+
${{ runner.os }}-
26+
27+
# Install required deps for action
28+
- name: Install Dependencies
29+
run: npm install
30+
31+
# Finally, run our tests
32+
- name: Run the tests
33+
run: npm test
34+
35+
- name: Tests ✅
36+
if: ${{ success() }}
37+
run: |
38+
curl --request POST \
39+
--url https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \
40+
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
41+
--header 'content-type: application/json' \
42+
--data '{
43+
"context": "tests",
44+
"state": "success",
45+
"description": "Tests passed",
46+
"target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
47+
}'
48+
49+
- name: Tests 🚨
50+
if: ${{ failure() }}
51+
run: |
52+
curl --request POST \
53+
--url https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \
54+
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
55+
--header 'content-type: application/json' \
56+
--data '{
57+
"context": "tests",
58+
"state": "failure",
59+
"description": "Tests failed",
60+
"target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
61+
}'

.vscode/launch.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Debug Jest Tests",
6+
"type": "node",
7+
"request": "launch",
8+
"runtimeArgs": [
9+
"--inspect-brk",
10+
"--experimental-vm-modules"
11+
],
12+
"program": "${workspaceRoot}/node_modules/.bin/jest",
13+
"args": [
14+
"--runInBand"
15+
],
16+
"skipFiles": [
17+
"<node_internals>/**"
18+
],
19+
"console": "integratedTerminal",
20+
"internalConsoleOptions": "neverOpen",
21+
},
22+
]
23+
}

domain/article.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export class Article {
1212
this.contentFilePath = contentFilePath;
1313
}
1414

15+
/**
16+
* Resets the cached properties.
17+
* This is useful if the content of an article is changed through one of the setters.
18+
*/
1519
clearData(){
1620
this._html = null;
1721
this._markdown = null;
@@ -21,6 +25,9 @@ export class Article {
2125
this._emphasizedTextData = null;
2226
}
2327

28+
/**
29+
* Returns the path of the folder containg an article
30+
*/
2431
get path(){
2532
if(this._basePath) return this._basePath;
2633
this._basePath = this._contentFilePath ? path.dirname(this._contentFilePath) : null;
@@ -57,6 +64,9 @@ export class Article {
5764
this._rawData = data;
5865
}
5966

67+
/**
68+
* Returns the raw text of an article document.
69+
*/
6070
get rawData(){
6171
if(this._rawData) return this._rawData;
6272

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ import { validateSyntaxSpecifiers } from './validations/code-blocks.js';
99
import { validateNestedLists } from './validations/lists.js';
1010
import { validateBrokenLinks } from './validations/links.js';
1111
import { ConfigManager } from './logic/config-manager.js';
12+
import { validateFolderName } from './validations/naming.js';
1213

13-
export { Validator, ArticleManager, validateDuplicatedOpeningHeading, validateHeadingsNesting, validateMaxLength, validateNumberedHeadings, validateOpeningHeadingLevel, validateSpacing, validateTitleCase, validateMetaData, validateRules, validateImageDescriptions, validateImagePaths, validateReferencedAssets, validateSVGFiles, validateSyntaxSpecifiers, validateNestedLists, validateBrokenLinks, ConfigManager }
14+
export { Validator, ArticleManager, validateDuplicatedOpeningHeading, validateHeadingsNesting, validateMaxLength, validateNumberedHeadings, validateOpeningHeadingLevel, validateSpacing, validateTitleCase, validateMetaData, validateRules, validateImageDescriptions, validateImagePaths, validateReferencedAssets, validateSVGFiles, validateSyntaxSpecifiers, validateNestedLists, validateBrokenLinks, ConfigManager, validateFolderName }

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "content-lint",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"type": "module",
55
"main": "index.js",
66
"description": "A node.js infrastructure to validate markdown content.",

test/metadata.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ articleA.rawData = contentA;
3636

3737
const articleB = new Article();
3838
articleB.rawData = contentB;
39-
const schemaPath = "./rules/tutorial-metadata-schema.json";
39+
const schemaPath = "./test/resources/tutorial-metadata-schema.json";
4040

4141
test('Tests if missing/superfluous property is detected', () => {
4242
const errors = validateMetaData(articleA, schemaPath);

test/naming.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Article } from '../domain/article.js'
2+
import { validateFolderName } from '../validations/naming.js';
3+
4+
const articleA = new Article();
5+
articleA.path = "./demo/this_is_Not_compliant";
6+
7+
const articleB = new Article();
8+
articleB.path = "./demo/this-should-pass";
9+
10+
test('Tests if non-compliant article folder name is detected', () => {
11+
const errors = validateFolderName(articleA);
12+
console.log(errors);
13+
expect(errors.length).toBe(2);
14+
});
15+
16+
test('Tests if compliant article folder name passes validation', () => {
17+
const errors = validateFolderName(articleB);
18+
console.log(errors);
19+
expect(errors.length).toBe(0);
20+
});
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"type": "object",
4+
"properties": {
5+
"beta": {
6+
"type": "boolean"
7+
},
8+
"source" : {
9+
"type": "string"
10+
},
11+
"title": {
12+
"type": "string",
13+
"maxLength": 60
14+
},
15+
"coverImage": {
16+
"type": "string"
17+
},
18+
"author": {
19+
"type": "string"
20+
},
21+
"tags": {
22+
"type": "array",
23+
"items": [
24+
{
25+
"type": "string"
26+
}
27+
]
28+
},
29+
"description": {
30+
"type": "string"
31+
},
32+
"difficulty": {
33+
"type": "string"
34+
},
35+
"hero_position": {
36+
"type": "integer"
37+
},
38+
"featuredImage": {
39+
"type": "string"
40+
},
41+
"software": {
42+
"type": "array",
43+
"items": [
44+
{
45+
"type": "string"
46+
}
47+
]
48+
},
49+
"compatible-products":{
50+
"type": "array",
51+
"items": [
52+
{
53+
"type": "string"
54+
}
55+
]
56+
},
57+
"hardware": {
58+
"type": "array",
59+
"items": [
60+
{
61+
"type": "string"
62+
}
63+
]
64+
},
65+
"libraries": {
66+
"type": "array",
67+
"items": [
68+
{
69+
"type": "object",
70+
"properties": {
71+
"name" : {
72+
"type" : "string"
73+
},
74+
"url" : {
75+
"type" : "string"
76+
}
77+
}
78+
}
79+
]
80+
}
81+
},
82+
"required": [
83+
"title",
84+
"tags",
85+
"description",
86+
"author"
87+
],
88+
"additionalProperties" : false
89+
}

validations/naming.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ValidationIssue } from '../domain/validation-issue.js';
2+
3+
/**
4+
* Checks if the foldername contains any discouraged underscores, spaces or upper-case letters.
5+
* @param {Article} article
6+
* @returns an array containg the occurred validation errors
7+
*/
8+
function validateFolderName(article) {
9+
let errorsOccurred = [];
10+
const issueType = ValidationIssue.Type.WARNING;
11+
12+
if(article.path.indexOf("_") != -1){
13+
const errorMessage = "Folder path uses discouraged underscore.";
14+
errorsOccurred.push(new ValidationIssue(errorMessage, article.path, issueType));
15+
}
16+
17+
if(article.path.indexOf(" ") != -1){
18+
const errorMessage = "Folder path uses discouraged blanks.";
19+
errorsOccurred.push(new ValidationIssue(errorMessage, article.path, issueType));
20+
}
21+
22+
if(article.path != article.path.toLowerCase()){
23+
const errorMessage = "Folder path uses discouraged non-lower case characters.";
24+
errorsOccurred.push(new ValidationIssue(errorMessage, article.path, issueType));
25+
}
26+
27+
return errorsOccurred;
28+
}
29+
30+
export { validateFolderName }

0 commit comments

Comments
 (0)