* Add W3C Markup Validation Service Badge (#3833) * Move helper functions into different file and added unit tests * Remove unnecessary comments from spec file * pr changes move code into transform method and validation of messages * use joi.string().regex instead of custom validator * Simplify the fetch, handle methods. Make Joi validation for string * Remove empty parameter from tests and label from render method * encodeUri on the doc and schema properties send to API * Documentation and remove unnecessary Object.keys call * Use regular expressions to make tests less brittle * made service less for message and color more generic and less brittle * Throw standard NoFound exception for invalid URL. Use w3c endpoint * use sazerac for w3c-validation-helper.spec.js * Replace documentation API url and API documentation url * Switch back to https://validator.nu endpoint. Remove html4 assertions * Increase strictness of NotFound checks
This commit is contained in:
156
services/w3c/w3c-validation-helper.js
Normal file
156
services/w3c/w3c-validation-helper.js
Normal file
@@ -0,0 +1,156 @@
|
||||
'use strict'
|
||||
|
||||
const html5Expression =
|
||||
'^HTML\\s?,\\s?SVG\\s?1\\.1\\s?,\\s?MathML\\s?3\\.0(\\s?,\\s?((ITS\\s?2\\.0)|(RDFa\\s?Lite\\s?1\\.1)))?$'
|
||||
const html4Expression =
|
||||
'^HTML\\s?4\\.01\\s?(Strict|Transitional|Frameset)\\s?,\\s?URL\\s?\\/\\s?XHTML\\s?1\\.0\\s?(Strict|Transitional|Frameset)\\s?,\\s?URL$'
|
||||
const xhtmlExpression =
|
||||
'^(XHTML\\s?,\\s?SVG\\s?1\\.1\\s?,\\s?MathML\\s?3\\.0(\\s?,\\s?RDFa\\s?Lite\\s?1\\.1)?)|(XHTML\\s?1\\.0\\s?Strict\\s?,\\s?URL\\s?,\\s?Ruby\\s?,\\s?SVG\\s?1\\.1\\s?,\\s?MathML\\s?3\\.0)$'
|
||||
const svgExpression =
|
||||
'^SVG\\s?1\\.1\\s?,\\s?URL\\s?,\\s?XHTML\\s?,\\s?MathML\\s?3\\.0$'
|
||||
const presetRegex = new RegExp(
|
||||
`(${html5Expression})|(${html4Expression})|(${xhtmlExpression})|(${svgExpression})`,
|
||||
'i'
|
||||
)
|
||||
|
||||
const getMessage = messageTypes => {
|
||||
const messageTypeKeys = Object.keys(messageTypes)
|
||||
messageTypeKeys.sort() // Sort to make the order error, warning for display
|
||||
|
||||
if (messageTypeKeys.length === 0) {
|
||||
return 'validated'
|
||||
}
|
||||
|
||||
const messages = messageTypeKeys.map(
|
||||
key => `${messageTypes[key]} ${key}${messageTypes[key] > 1 ? 's' : ''}`
|
||||
)
|
||||
return messages.join(', ')
|
||||
}
|
||||
|
||||
const getColor = messageTypes => {
|
||||
if ('error' in messageTypes) {
|
||||
return 'red'
|
||||
}
|
||||
|
||||
if ('warning' in messageTypes) {
|
||||
return 'yellow'
|
||||
}
|
||||
|
||||
return 'brightgreen'
|
||||
}
|
||||
|
||||
const getSchema = preset => {
|
||||
if (!preset) return undefined
|
||||
const decodedPreset = decodeURI(preset)
|
||||
const schema = []
|
||||
if (new RegExp(html4Expression, 'i').test(decodedPreset)) {
|
||||
if (/Strict/i.test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/xhtml10/xhtml-strict.rnc')
|
||||
} else if (/Transitional/i.test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/xhtml10/xhtml-transitional.rnc')
|
||||
} else {
|
||||
schema.push('http://s.validator.nu/xhtml10/xhtml-frameset.rnc')
|
||||
}
|
||||
schema.push('http://c.validator.nu/all-html4/')
|
||||
} else if (/1\.0 Strict, URL, Ruby, SVG 1\.1/i.test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/xhtml1-ruby-rdf-svg-mathml.rnc')
|
||||
schema.push('http://c.validator.nu/all-html4/')
|
||||
} else {
|
||||
if (new RegExp(html5Expression, 'i').test(decodedPreset)) {
|
||||
if (/ITS 2\.0/i.test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/html5-its.rnc')
|
||||
} else if (/RDFa Lite 1\.1/i.test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/html5-rdfalite.rnc')
|
||||
} else {
|
||||
schema.push('http://s.validator.nu/html5.rnc')
|
||||
}
|
||||
} else if (new RegExp(xhtmlExpression, 'i').test(decodedPreset)) {
|
||||
if (/RDFa Lite 1\.1/i.test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/xhtml5-rdfalite.rnc')
|
||||
} else {
|
||||
schema.push('http://s.validator.nu/xhtml5.rnc')
|
||||
}
|
||||
} else if (new RegExp(svgExpression, 'i').test(decodedPreset)) {
|
||||
schema.push('http://s.validator.nu/svg-xhtml5-rdf-mathml.rnc')
|
||||
}
|
||||
schema.push('http://s.validator.nu/html5/assertions.sch')
|
||||
schema.push('http://c.validator.nu/all/')
|
||||
}
|
||||
return schema.map(url => encodeURI(url)).join(' ')
|
||||
}
|
||||
|
||||
const documentation = `
|
||||
<style>
|
||||
.box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.note {
|
||||
font-size: smaller;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
<p>
|
||||
The W3C validation badge performs validation of the HTML, SVG, MathML, ITS, RDFa Lite, XHTML documents.
|
||||
The badge uses the type property of each message found in the messages from the validation results to determine to be an error or warning.
|
||||
The rules are as follows:
|
||||
<ul class="note">
|
||||
<li>info: These messages are counted as warnings</li>
|
||||
<li>error: These messages are counted as errors</li>
|
||||
<li>non-document-error: These messages are counted as errors</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
This badge relies on the https://validator.nu/ service to perform the validation. Please refer to https://about.validator.nu/ for the full documentation and Terms of service.
|
||||
The following are required from the consumer for the badge to function.
|
||||
|
||||
<ul class="note">
|
||||
<li>
|
||||
Path:
|
||||
<ul>
|
||||
<li>
|
||||
parser: The parser that is used for validation. This is a passthru value to the service
|
||||
<ul>
|
||||
<li>default <i>(This will not pass a parser to the API and make the API choose the parser based on the validated content)</i></li>
|
||||
<li>html <i>(HTML)</i></li>
|
||||
<li>xml <i>(XML; don’t load external entities)</i></li>
|
||||
<li>xmldtd <i>(XML; load external entities)</i></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Query string:
|
||||
<ul>
|
||||
<li>
|
||||
targetUrl (Required): This is the path for the document to be validated
|
||||
</li>
|
||||
<li>
|
||||
preset (Optional can be left as blank): This is used to determine the schema for the document to be valdiated against.
|
||||
The following are the allowed values
|
||||
<ul>
|
||||
<li>HTML, SVG 1.1, MathML 3.0</li>
|
||||
<li>HTML, SVG 1.1, MathML 3.0, ITS 2.0</li>
|
||||
<li>HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1</li>
|
||||
<li>HTML 4.01 Strict, URL / XHTML 1.0 Strict, URL</li>
|
||||
<li>HTML 4.01 Transitional, URL / XHTML 1.0 Transitional, URL</li>
|
||||
<li>HTML 4.01 Frameset, URL / XHTML 1.0 Frameset, URL</li>
|
||||
<li>XHTML, SVG 1.1, MathML 3.0</li>
|
||||
<li>XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1</li>
|
||||
<li>XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0</li>
|
||||
<li>SVG 1.1, URL, XHTML, MathML 3.0</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
`
|
||||
|
||||
module.exports = {
|
||||
documentation,
|
||||
presetRegex,
|
||||
getColor,
|
||||
getMessage,
|
||||
getSchema,
|
||||
}
|
||||
265
services/w3c/w3c-validation-helper.spec.js
Normal file
265
services/w3c/w3c-validation-helper.spec.js
Normal file
@@ -0,0 +1,265 @@
|
||||
'use strict'
|
||||
const { expect } = require('chai')
|
||||
const { test, given, forCases } = require('sazerac')
|
||||
const {
|
||||
presetRegex,
|
||||
getMessage,
|
||||
getColor,
|
||||
getSchema,
|
||||
} = require('./w3c-validation-helper')
|
||||
|
||||
describe('w3c-validation-helper', function() {
|
||||
describe('presetRegex', function() {
|
||||
function testing(preset) {
|
||||
return presetRegex.test(preset)
|
||||
}
|
||||
|
||||
test(testing, () => {
|
||||
forCases([
|
||||
given('html,svg 1.1,mathml 3.0'),
|
||||
given('HTML,SVG 1.1,MathML 3.0'),
|
||||
given('HTML, SVG 1.1, MathML 3.0'),
|
||||
given('HTML , SVG 1.1 , MathML 3.0'),
|
||||
given('HTML,SVG 1.1,MathML 3.0,ITS 2.0'),
|
||||
given('HTML, SVG 1.1, MathML 3.0, ITS 2.0'),
|
||||
given('HTML , SVG 1.1 , MathML 3.0 , ITS 2.0'),
|
||||
given('HTML,SVG 1.1,MathML 3.0,RDFa Lite 1.1'),
|
||||
given('HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1'),
|
||||
given('HTML , SVG 1.1 , MathML 3.0 , RDFa Lite 1.1'),
|
||||
given('HTML 4.01 Strict,URL/XHTML 1.0 Strict,URL'),
|
||||
given('HTML 4.01 Strict, URL/ XHTML 1.0 Strict, URL'),
|
||||
given('HTML 4.01 Strict , URL / XHTML 1.0 Strict , URL'),
|
||||
given('HTML 4.01 Transitional,URL/XHTML 1.0 Transitional,URL'),
|
||||
given('HTML 4.01 Transitional, URL/ XHTML 1.0 Transitional, URL'),
|
||||
given('HTML 4.01 Transitional , URL / XHTML 1.0 Transitional , URL'),
|
||||
given('HTML 4.01 Frameset,URL/XHTML 1.0 Frameset,URL'),
|
||||
given('HTML 4.01 Frameset, URL/ XHTML 1.0 Frameset, URL'),
|
||||
given('HTML 4.01 Frameset , URL / XHTML 1.0 Frameset , URL'),
|
||||
given('XHTML,SVG 1.1,MathML 3.0'),
|
||||
given('XHTML, SVG 1.1, MathML 3.0'),
|
||||
given('XHTML , SVG 1.1 , MathML 3.0'),
|
||||
given('XHTML,SVG 1.1,MathML 3.0,RDFa Lite 1.1'),
|
||||
given('XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1'),
|
||||
given('XHTML , SVG 1.1 , MathML 3.0 , RDFa Lite 1.1'),
|
||||
given('XHTML 1.0 Strict,URL,Ruby,SVG 1.1,MathML 3.0'),
|
||||
given('XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0'),
|
||||
given('XHTML 1.0 Strict , URL , Ruby , SVG 1.1 , MathML 3.0'),
|
||||
given('SVG 1.1,URL,XHTML,MathML 3.0'),
|
||||
given('SVG 1.1, URL, XHTML, MathML 3.0'),
|
||||
given('SVG 1.1 , URL , XHTML , MathML 3.0'),
|
||||
]).expect(true)
|
||||
})
|
||||
|
||||
test(testing, () => {
|
||||
forCases([
|
||||
given(undefined),
|
||||
given(null),
|
||||
given(''),
|
||||
given(' '),
|
||||
given('HTML'),
|
||||
]).expect(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getColor', function() {
|
||||
it('returns "brightgreen" if no messages are provided', function() {
|
||||
const messageTypes = {}
|
||||
|
||||
const actualResult = getColor(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('brightgreen')
|
||||
})
|
||||
|
||||
it('returns "yellow" if only warning messages are provided', function() {
|
||||
const messageTypes = { warning: 1 }
|
||||
|
||||
const actualResult = getColor(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('yellow')
|
||||
})
|
||||
|
||||
it('returns "red" if only error messages are provided', function() {
|
||||
const messageTypes = { error: 1 }
|
||||
|
||||
const actualResult = getColor(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('red')
|
||||
})
|
||||
|
||||
it('returns "red" if both warning and error messages are provided', function() {
|
||||
const messageTypes = { warning: 3, error: 4 }
|
||||
|
||||
const actualResult = getColor(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('red')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getMessage', function() {
|
||||
it('returns "validate" if no messages are provided', function() {
|
||||
const messageTypes = {}
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('validated')
|
||||
})
|
||||
|
||||
it('returns "1 error" if 1 error message is provided', function() {
|
||||
const messageTypes = { error: 1 }
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('1 error')
|
||||
})
|
||||
|
||||
it('returns "2 errors" if 2 error messages are provided', function() {
|
||||
const messageTypes = { error: 2 }
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('2 errors')
|
||||
})
|
||||
|
||||
it('returns "1 warning" if 1 warning message is provided', function() {
|
||||
const messageTypes = { warning: 1 }
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('1 warning')
|
||||
})
|
||||
|
||||
it('returns "2 warnings" if 2 warning messages are provided', function() {
|
||||
const messageTypes = { warning: 2 }
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('2 warnings')
|
||||
})
|
||||
|
||||
it('returns "1 error, 1 warning" if 1 error and 1 warning message is provided', function() {
|
||||
const messageTypes = { warning: 1, error: 1 }
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('1 error, 1 warning')
|
||||
})
|
||||
|
||||
it('returns "2 errors, 2 warnings" if 2 error and 2 warning message is provided', function() {
|
||||
const messageTypes = { error: 2, warning: 2 }
|
||||
|
||||
const actualResult = getMessage(messageTypes)
|
||||
|
||||
expect(actualResult).to.equal('2 errors, 2 warnings')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getSchema', function() {
|
||||
function execution(preset) {
|
||||
return getSchema(preset)
|
||||
}
|
||||
|
||||
test(execution, () => {
|
||||
forCases([given(undefined), given(null), given('')]).expect(undefined)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "HTML,SVG 1.1,MathML 3.0" preset', function() {
|
||||
const preset = 'HTML,SVG 1.1,MathML 3.0'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/html5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "HTML,SVG 1.1,MathML 3.0,ITS 2.0" preset', function() {
|
||||
const preset = 'HTML,SVG 1.1,MathML 3.0,ITS 2.0'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/html5-its.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1" preset', function() {
|
||||
const preset = 'HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/html5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "HTML 4.01 Strict, URL/ XHTML 1.0 Strict, URL" preset', function() {
|
||||
const preset = 'HTML 4.01 Strict, URL/ XHTML 1.0 Strict, URL'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/xhtml10/xhtml-strict.rnc http://c.validator.nu/all-html4/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "HTML 4.01 Transitional, URL/ XHTML 1.0 Transitional, URL" preset', function() {
|
||||
const preset = 'HTML 4.01 Transitional, URL/ XHTML 1.0 Transitional, URL'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://c.validator.nu/all-html4/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "HTML 4.01 Frameset, URL/ XHTML 1.0 Frameset, URL" preset', function() {
|
||||
const preset = 'HTML 4.01 Frameset, URL/ XHTML 1.0 Frameset, URL'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/xhtml10/xhtml-frameset.rnc http://c.validator.nu/all-html4/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "XHTML, SVG 1.1, MathML 3.0" preset', function() {
|
||||
const preset = 'XHTML, SVG 1.1, MathML 3.0'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/xhtml5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1" preset', function() {
|
||||
const preset = 'XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/xhtml5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0" preset', function() {
|
||||
const preset = 'XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/xhtml1-ruby-rdf-svg-mathml.rnc http://c.validator.nu/all-html4/'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 3 schemas associated to the "SVG 1.1, URL, XHTML, MathML 3.0" preset', function() {
|
||||
const preset = 'SVG 1.1, URL, XHTML, MathML 3.0'
|
||||
|
||||
const actualResult = getSchema(preset)
|
||||
|
||||
expect(actualResult).to.equal(
|
||||
'http://s.validator.nu/svg-xhtml5-rdf-mathml.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
136
services/w3c/w3c-validation.service.js
Normal file
136
services/w3c/w3c-validation.service.js
Normal file
@@ -0,0 +1,136 @@
|
||||
'use strict'
|
||||
const Joi = require('@hapi/joi')
|
||||
const { optionalUrl } = require('../validators')
|
||||
const {
|
||||
documentation,
|
||||
presetRegex,
|
||||
getColor,
|
||||
getMessage,
|
||||
getSchema,
|
||||
} = require('./w3c-validation-helper')
|
||||
const { BaseJsonService, NotFound } = require('..')
|
||||
|
||||
const schema = Joi.object({
|
||||
url: Joi.string().optional(),
|
||||
messages: Joi.array()
|
||||
.required()
|
||||
.items(
|
||||
Joi.object({
|
||||
type: Joi.string()
|
||||
.allow('info', 'error', 'non-document-error')
|
||||
.required(),
|
||||
subType: Joi.string().optional(),
|
||||
message: Joi.string().required(),
|
||||
})
|
||||
),
|
||||
}).required()
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
targetUrl: optionalUrl.required(),
|
||||
preset: Joi.string()
|
||||
.regex(presetRegex)
|
||||
.allow(''),
|
||||
}).required()
|
||||
|
||||
module.exports = class W3cValidation extends BaseJsonService {
|
||||
static get category() {
|
||||
return 'analysis'
|
||||
}
|
||||
|
||||
static get route() {
|
||||
return {
|
||||
base: 'w3c-validation',
|
||||
pattern: ':parser(default|html|xml|xmldtd)',
|
||||
queryParamSchema,
|
||||
}
|
||||
}
|
||||
|
||||
static get examples() {
|
||||
return [
|
||||
{
|
||||
title: 'W3C Validation',
|
||||
namedParams: { parser: 'html' },
|
||||
queryParams: {
|
||||
targetUrl: 'https://validator.nu/',
|
||||
preset: 'HTML, SVG 1.1, MathML 3.0',
|
||||
},
|
||||
staticPreview: this.render({ messageTypes: {} }),
|
||||
documentation,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static get defaultBadgeData() {
|
||||
return {
|
||||
label: 'w3c',
|
||||
}
|
||||
}
|
||||
|
||||
static render({ messageTypes }) {
|
||||
return {
|
||||
message: getMessage(messageTypes),
|
||||
color: getColor(messageTypes),
|
||||
}
|
||||
}
|
||||
|
||||
async fetch(targetUrl, preset, parser) {
|
||||
return this._requestJson({
|
||||
url: 'https://validator.nu/',
|
||||
schema,
|
||||
options: {
|
||||
qs: {
|
||||
schema: getSchema(preset),
|
||||
parser: parser === 'default' ? undefined : parser,
|
||||
doc: encodeURI(targetUrl),
|
||||
out: 'json',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
transform(url, messages) {
|
||||
if (messages.length === 1) {
|
||||
const { subType, type, message } = messages[0]
|
||||
if (type === 'non-document-error' && subType === 'io') {
|
||||
let notFound = false
|
||||
if (
|
||||
message ===
|
||||
'HTTP resource not retrievable. The HTTP status from the remote server was: 404.'
|
||||
) {
|
||||
notFound = true
|
||||
} else if (message.endsWith('Name or service not known')) {
|
||||
const domain = message.split(':')[0].trim()
|
||||
notFound = url.indexOf(domain) !== -1
|
||||
}
|
||||
|
||||
if (notFound) {
|
||||
throw new NotFound({ prettyMessage: 'target url not found' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages.reduce((accumulator, message) => {
|
||||
let { type } = message
|
||||
if (type === 'info') {
|
||||
type = 'warning'
|
||||
} else {
|
||||
// All messages are suppose to have a type and there can only be info, error or non-document
|
||||
// If a new type gets introduce this will flag them as errors
|
||||
type = 'error'
|
||||
}
|
||||
|
||||
if (!(type in accumulator)) {
|
||||
accumulator[type] = 0
|
||||
}
|
||||
accumulator[type] += 1
|
||||
return accumulator
|
||||
}, {})
|
||||
}
|
||||
|
||||
async handle({ parser }, { targetUrl, preset }) {
|
||||
const { url, messages } = await this.fetch(targetUrl, preset, parser)
|
||||
return this.constructor.render({
|
||||
messageTypes: this.transform(url, messages),
|
||||
})
|
||||
}
|
||||
}
|
||||
100
services/w3c/w3c-validation.tester.js
Normal file
100
services/w3c/w3c-validation.tester.js
Normal file
@@ -0,0 +1,100 @@
|
||||
'use strict'
|
||||
const Joi = require('@hapi/joi')
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
|
||||
const isErrorOnly = Joi.string().regex(/^[0-9]+ errors?$/)
|
||||
|
||||
const isWarningOnly = Joi.string().regex(/^[0-9]+ warnings?$/)
|
||||
|
||||
const isErrorAndWarning = Joi.string().regex(
|
||||
/^[0-9]+ errors?, [0-9]+ warnings?$/
|
||||
)
|
||||
|
||||
const isW3CMessage = Joi.alternatives().try(
|
||||
'validated',
|
||||
isErrorOnly,
|
||||
isWarningOnly,
|
||||
isErrorAndWarning
|
||||
)
|
||||
const isW3CColors = Joi.alternatives().try('brightgreen', 'red', 'yellow')
|
||||
t.create(
|
||||
'W3C Validation page conforms to standards with no preset and parser with brightgreen badge'
|
||||
)
|
||||
.get(
|
||||
'/default.json?targetUrl=https://hsivonen.com/test/moz/messages-types/no-message.html'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: isW3CMessage,
|
||||
color: isW3CColors,
|
||||
})
|
||||
|
||||
t.create(
|
||||
'W3C Validation page conforms to standards with no HTML4 preset and HTML parser with brightgreen badge'
|
||||
)
|
||||
.get(
|
||||
'/html.json?targetUrl=https://hsivonen.com/test/moz/messages-types/no-message.html&preset=HTML,%20SVG%201.1,%20MathML%203.0'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: isW3CMessage,
|
||||
color: isW3CColors,
|
||||
})
|
||||
|
||||
t.create('W3C Validation target url not found error')
|
||||
.get(
|
||||
'/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/404.html'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: 'target url not found',
|
||||
})
|
||||
|
||||
t.create('W3C Validation target url host not found error')
|
||||
.get('/default.json?targetUrl=https://adfasdfasdfasdfadfadfadfasdfadf.com')
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: 'target url not found',
|
||||
})
|
||||
|
||||
t.create('W3C Validation page has 1 validation error with red badge')
|
||||
.get(
|
||||
'/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/warning.html'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: isW3CMessage,
|
||||
color: isW3CColors,
|
||||
})
|
||||
|
||||
t.create(
|
||||
'W3C Validation page has 3 validation error using HTML 4.01 Frameset preset with red badge'
|
||||
)
|
||||
.get(
|
||||
'/html.json?targetUrl=http://hsivonen.com/test/moz/messages-types/warning.html&preset=HTML 4.01 Frameset, URL / XHTML 1.0 Frameset, URL'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: isW3CMessage,
|
||||
color: isW3CColors,
|
||||
})
|
||||
|
||||
t.create('W3C Validation page has 1 validation warning with yellow badge')
|
||||
.get(
|
||||
'/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/info.svg'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: isW3CMessage,
|
||||
color: isW3CColors,
|
||||
})
|
||||
|
||||
t.create('W3C Validation page has multiple of validation errors with red badge')
|
||||
.get(
|
||||
'/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/range-error.html'
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'w3c',
|
||||
message: isW3CMessage,
|
||||
color: isW3CColors,
|
||||
})
|
||||
Reference in New Issue
Block a user