This repository has been archived by the owner on Jun 7, 2024. It is now read-only.
forked from speced/respec
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core/dfn-validators): add dfn type validators (speced#3700)
- Loading branch information
1 parent
69edf29
commit 7f0e6d7
Showing
7 changed files
with
226 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { MIMEType } from "./import-maps.js"; | ||
import { showError } from "./utils.js"; | ||
|
||
/** | ||
* Validates MIME types strings. | ||
* | ||
* @type {DefinitionValidator} */ | ||
export function validateMimeType(text, type, elem, pluginName) { | ||
try { | ||
// Constructor can throw. | ||
const type = new MIMEType(text); | ||
if (type.toString() !== text) { | ||
throw new Error(`Input doesn't match its canonical form: "${type}".`); | ||
} | ||
} catch (error) { | ||
const msg = `Invalid ${type} "${text}": ${error.message}.`; | ||
const hint = | ||
"Check that the MIME type has both a type and a sub-type, and that it's in a canonical form (e.g., `text/plain`)."; | ||
showError(msg, pluginName, { hint, elements: [elem] }); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Validates the names of DOM attribute and elements. | ||
* @param {"attribute" | "element"} type | ||
* @type {DefinitionValidator} */ | ||
export function validateDOMName(text, type, elem, pluginName) { | ||
try { | ||
switch (type) { | ||
case "attribute": | ||
document.createAttribute(text); | ||
return true; | ||
case "element": | ||
document.createElement(text); | ||
return true; | ||
} | ||
} catch (err) { | ||
const msg = `Invalid ${type} name "${text}": ${err.message}`; | ||
const hint = `Check that the ${type} name is allowed per the XML's Name production for ${type}.`; | ||
showError(msg, pluginName, { hint, elements: [elem] }); | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* Validates common variable or other named thing in a spec, like event names. | ||
* | ||
* @type {DefinitionValidator} | ||
*/ | ||
export function validateCommonName(text, type, elem, pluginName) { | ||
// Check a-z, maybe a dash and letters, case insensitive. | ||
// Also, no spaces. | ||
if (/^[a-z]+(-[a-z]+)*$/i.test(text)) { | ||
return true; // all good | ||
} | ||
const msg = `Invalid ${type} name "${text}"`; | ||
const hint = `Check that the ${type} name is allowed`; | ||
showError(msg, pluginName, { hint, elements: [elem] }); | ||
return false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { | ||
validateCommonName, | ||
validateDOMName, | ||
validateMimeType, | ||
} from "../../../src/core/dfn-validators.js"; | ||
|
||
describe("Core - Validators", () => { | ||
describe("validateDOMName", () => { | ||
it("doesn't generate an error if the element is valid", () => { | ||
const elements = [ | ||
"h1", | ||
"feSpecularLighting", | ||
"feGaussianBlur", | ||
"body", | ||
"html", | ||
]; | ||
for (const element of elements) { | ||
const dfn = document.createElement("dfn"); | ||
const context = `element name: ${element}`; | ||
expect(validateDOMName(element, "element", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
} | ||
}); | ||
|
||
it("generates an error if the element name is not valid", () => { | ||
const elements = ["my element", "crypto$", "🪳", "-something", ""]; | ||
for (const element of elements) { | ||
const dfn = document.createElement("dfn"); | ||
const context = `element name: ${element}`; | ||
expect(validateDOMName(element, "element", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
} | ||
}); | ||
|
||
it("doesn't generates an error if the attribute name is valid", () => { | ||
const attributes = ["crossorigin", "aria-hidden", "aria-roledescription"]; | ||
for (const attribute of attributes) { | ||
const context = `attribute name: ${attribute}`; | ||
const dfn = document.createElement("dfn"); | ||
expect(validateDOMName(attribute, "attribute", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
} | ||
}); | ||
|
||
it("generates an error if the attribute name is invalid", () => { | ||
const attributes = ["-crossorigin", "-whatever-", "aria-😇"]; | ||
for (const attribute of attributes) { | ||
const context = `attribute name: ${attribute}`; | ||
const dfn = document.createElement("dfn"); | ||
expect(validateDOMName(attribute, "attribute", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
} | ||
}); | ||
}); | ||
|
||
describe("validateMimeType", () => { | ||
it("generates no error if the mimeType is valid", () => { | ||
const mimeTypes = [ | ||
"image/svg+xml", | ||
"application/x-font-woff", | ||
"image/jpeg", | ||
"text/plain", | ||
]; | ||
for (const mimeType of mimeTypes) { | ||
const dfn = document.createElement("dfn"); | ||
const context = `mimeType: ${mimeType}`; | ||
expect(validateMimeType(mimeType, "mimetype", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
} | ||
}); | ||
|
||
it("generates error if the mimeType is not valid", () => { | ||
const mimeTypes = [ | ||
"text\\plain", | ||
"not a mimetype", | ||
"a/b;c=this is quoted;e=f", | ||
]; | ||
for (const mimeType of mimeTypes) { | ||
const dfn = document.createElement("dfn"); | ||
const context = `invalid mimeType: ${mimeType}`; | ||
expect(validateMimeType(mimeType, "mimetype", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
} | ||
}); | ||
}); | ||
|
||
describe("validateCommonName", () => { | ||
it("generates no error if the name is valid", () => { | ||
const names = ["foo", "bar", "baz", "quux"]; | ||
for (const name of names) { | ||
const dfn = document.createElement("dfn"); | ||
const context = `name: ${name}`; | ||
expect(validateCommonName(name, "event", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
} | ||
}); | ||
|
||
it("it generates an error if the name is not valid", () => { | ||
const names = ["my event", "crypto$", "🪳", "-something"]; | ||
for (const name of names) { | ||
const dfn = document.createElement("dfn"); | ||
const context = `invalid name: ${name}`; | ||
expect(validateCommonName(name, "event", dfn, "foo/bar")) | ||
.withContext(context) | ||
.toBeFalse(); | ||
expect(dfn.classList.contains("respec-offending-element")) | ||
.withContext(context) | ||
.toBeTrue(); | ||
} | ||
}); | ||
}); | ||
}); |