Added CSS color utilities and respective tests.

Installed mocha and chai for testing.

Changed package versions to avoid vulnerabilities.
This commit is contained in:
Harrison Deng 2022-04-17 01:27:00 -05:00
parent d46f30273d
commit cb72a387aa
5 changed files with 1256 additions and 271 deletions

5
.gitignore vendored
View File

@ -493,4 +493,7 @@ dist
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
# Don't include HTML documentation. # Don't include HTML documentation.
docs docs
# Don't include junit coverage report.
junit

1286
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
"scripts": { "scripts": {
"build": "webpack --config webpack.prod.js", "build": "webpack --config webpack.prod.js",
"watch": "webpack serve --config webpack.dev.js", "watch": "webpack serve --config webpack.dev.js",
"test": "echo \"No test specified yet...\" && exit 0", "test": "mocha tests/** --reporter mocha-junit-reporter --reporter-options mochaFile=./junit/test_results.xml",
"docs": "jsdoc src -r -d docs -c jsdoc.config.json" "docs": "jsdoc src -r -d docs -c jsdoc.config.json"
}, },
"author": "", "author": "",
@ -16,8 +16,9 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.17.9", "@babel/core": "^7.17.9",
"@babel/preset-env": "^7.16.11", "@babel/preset-env": "^7.16.11",
"@ckeditor/jsdoc-plugins": "^30.1.3", "@ckeditor/jsdoc-plugins": "^20.0.0",
"babel-loader": "^8.2.4", "babel-loader": "^8.2.4",
"chai": "^4.3.6",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"eslint": "^8.13.0", "eslint": "^8.13.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
@ -25,6 +26,8 @@
"html-loader": "^3.1.0", "html-loader": "^3.1.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"jsdoc": "^3.6.10", "jsdoc": "^3.6.10",
"mocha": "^9.2.2",
"mocha-junit-reporter": "^2.0.2",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"webpack": "^5.72.0", "webpack": "^5.72.0",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",

102
src/mapping/colors.js Normal file
View File

@ -0,0 +1,102 @@
/**
* The rg
*
* @param {string} hex the hex value.
* @returns {number[]} the resulting components of the hex value.
*/
export function rgbaHexToRgba(hex) {
if (hex.startsWith("#")) {
hex = hex.substring(1);
if (hex.length > 8) {
throw new Error("Invalid hex syntax (length).");
}
} else {
throw new Error("Invalid hex syntax (missing pound).");
}
for (let i = hex.length; i < 8; i++) {
hex = "0" + hex;
}
let remaining = hex.length;
let result = [0, 0, 0, 0];
result[3] = parseInt(hex.substring(remaining -= 2, remaining + 2), 16);
result[2] = parseInt(hex.substring(remaining -= 2, remaining + 2), 16);
result[1] = parseInt(hex.substring(remaining -= 2, remaining + 2), 16);
result[0] = parseInt(hex.substring(remaining -= 2, remaining + 2), 16);
return result;
}
/**
*
* @param {string} hex the hex value.
* @returns {number[]} the resulting r, g, and b components.
*/
export function rgbHexToRgba(hex) {
const result = rgbaHexToRgba(hex + "FF");
return result;
}
/**
*
* @param {string} rgba The CSS rgba(r,g,b,a) call.
* @returns {number[]} the rgba components.
*/
export function cssRgbaToRgba(rgba) {
const cssRgbaRegex = /rgba\((\d+),(\d+),(\d+),(\d+)\)/;
try {
const matches = rgba.match(cssRgbaRegex);
return [parseInt(matches[1]), parseInt(matches[2]), parseInt(matches[3]), parseInt(matches[4])];
} catch (error) {
throw new Error("Could not parse the given css rgba function call: " + error.message);
}
}
/**
*
* @param {string} rgb The CSS rgb(r,g,b) call.
* @returns {number[]} the rgba components.
*/
export function cssRgbToRgba(rgb) {
const cssRgbRegex = /rgb\((\d+),(\d+),(\d+)\)/;
try {
const matches = rgb.match(cssRgbRegex);
return [parseInt(matches[1]), parseInt(matches[2]), parseInt(matches[3]), 255];
} catch (error) {
throw new Error("Could not parse the given css rgb function call: " + error.message);
}
}
/**
* Converts a given array of r, g, b, and a components into a hex string.
*
* @param {number[]} rgba an array with red, green, blue, and alpha components in that order.
* @returns {string} The resulting hex value.
*/
export function rgbaToHexRgba(rgba) {
const filler = (hex) => hex.length < 2 ? "0" + hex : hex;
return "#" + filler(rgba[0].toString(16)) + filler(rgba[1].toString(16)) + filler(rgba[2].toString(16)) + filler(rgba[3].toString(16));
}
/**
* Converts a css rgb, rgba, hex, or rgba hex to a rgba array.
*
* For hex, we assume there is no alpha channel unless the hex value is not minimized.
*
* @param {string} color The string that contains the color information.
* @returns {number[]} an array that contains the r, g, b and a components.
*/
export function parseColor(color) {
if (color.startsWith("rgba(")) {
return cssRgbaToRgba(color);
} else if (color.startsWith("rgb(")) {
return cssRgbToRgba(color);
} else if (color.startsWith("#")) {
if (color.length === 9) {
return rgbaHexToRgba(color);
} else {
return rgbHexToRgba(color);
}
}
throw new Error("Could not parse to an rgba value.");
}

127
tests/testcolors.js Normal file
View File

@ -0,0 +1,127 @@
/* eslint-disable no-undef */
import { expect } from "chai";
import { describe } from "mocha";
import { cssRgbaToRgba, cssRgbToRgba, rgbaHexToRgba, parseColor, rgbaToHexRgba, rgbHexToRgba } from "../src/mapping/colors.js";
describe("Color utilities", function () {
describe("the hex to rgba conversion function", function () {
it("returns 0, 0, 0 and 1 for r, g, b and a given \"#01\"", function () {
const rgba = rgbaHexToRgba("#01");
expect(rgba).to.have.ordered.members([0, 0, 0, 1]);
});
it("returns 0, 0, 1 and 1 for r, g, b, and a given \"#0101\"", function () {
const rgba = rgbaHexToRgba("#0101");
expect(rgba).to.have.ordered.members([0, 0, 1, 1]);
});
it("returns 0, 1, 1 and 0 for r, g, b and a given \"#10100\"", function () {
const rgba = rgbaHexToRgba("#10100");
expect(rgba).to.have.ordered.members([0, 1, 1, 0]);
});
it("returns 255, 0, 0 and 0 for r, g, b and a given \"#FF000000\"", function () {
const rgba = rgbaHexToRgba("#FF000000");
expect(rgba).to.have.ordered.members([255, 0, 0, 0]);
});
it("returns 255, 255, 0 and 0 for r, g, b and a given \"#FFFF0000\"", function () {
const rgba = rgbaHexToRgba("#FFFF0000");
expect(rgba).to.have.ordered.members([255, 255, 0, 0]);
});
});
describe("the hex to rgba conversion function (alpha will always be 255)", function () {
it("returns 0, 0 and 1 for r, g and b given \"#1\"", function () {
const rgb = rgbHexToRgba("#1");
expect(rgb).to.have.ordered.members([0, 0, 1, 255]);
});
it("returns 0, 1 and 0 for r, g and b given \"#100\"", function () {
const rgb = rgbHexToRgba("#100");
expect(rgb).to.have.ordered.members([0, 1, 0, 255]);
});
it("returns 1, 0 and 1 for r, g and b given \"#10001\"", function () {
const rgb = rgbHexToRgba("#10001");
expect(rgb).to.have.ordered.members([1, 0, 1, 255]);
});
});
describe("the css rgba function to rgba function", function () {
it("returns 0, 0, 0 and 1 for r, g, b and a given \"rgba(0,0,0,1)\"", function () {
const rgba = cssRgbaToRgba("rgba(0,0,0,1)");
expect(rgba).to.have.ordered.members([0, 0, 0, 1]);
});
it("returns 0, 0, 1 and 1 for r, g, b, and a given \"#rgba(0,0,1,1)\"", function () {
const rgba = cssRgbaToRgba("#rgba(0,0,1,1)");
expect(rgba).to.have.ordered.members([0, 0, 1, 1]);
});
it("returns 0, 1, 1 and 0 for r, g, b and a given \"rgba(0,1,1,0)\"", function () {
const rgba = cssRgbaToRgba("rgba(0,1,1,0)");
expect(rgba).to.have.ordered.members([0, 1, 1, 0]);
});
it("returns 255, 0, 0 and 0 for r, g, b and a given \"rgba(255,0,0,0)\"", function () {
const rgba = cssRgbaToRgba("rgba(255,0,0,0)");
expect(rgba).to.have.ordered.members([255, 0, 0, 0]);
});
});
describe("the css rgb function to rgba function (alpha will always be 255)", function () {
it("returns 0, 0 and 1 for r, g and b given \"rgb(0,0,1)\"", function () {
const rgba = cssRgbToRgba("rgb(0,0,1)");
expect(rgba).to.have.ordered.members([0, 0, 1, 255]);
});
it("returns 0, 1 and 1 for r, g and b given \"#rgb(0,1,0)\"", function () {
const rgba = cssRgbToRgba("#rgb(0,1,0)");
expect(rgba).to.have.ordered.members([0, 1, 0, 255]);
});
it("returns 1, 1 and 1 for r, g and b given \"rgb(1,1,1)\"", function () {
const rgba = cssRgbToRgba("rgb(1,1,1)");
expect(rgba).to.have.ordered.members([1, 1, 1, 255]);
});
});
describe("The function to convert r, g, b, and a represented as an array to hexadecimals", function () {
it("Returns #00000001 when given an array of [0, 0, 0, 1]", function () {
const hex = rgbaToHexRgba([0, 0, 0, 1]);
expect(hex).to.equal("#00000001");
});
it("Returns #00000100 when given an array of [0, 0, 1, 0]", function () {
const hex = rgbaToHexRgba([0, 0, 1, 0]);
expect(hex).to.equal("#00000100");
});
it("Returns #00010000 when given an array of [0, 1, 0, 0]", function () {
const hex = rgbaToHexRgba([0, 1, 0, 0]);
expect(hex).to.equal("#00010000");
});
it("Returns #01000000 when given an array of [1, 0, 0, 0]", function () {
const hex = rgbaToHexRgba([1, 0, 0, 0]);
expect(hex).to.equal("#01000000");
});
});
describe("The function that automatically converts a string containing an rgb(a) value to an rgba array.", function () {
it("returns 1, 1, 1 and 1 for r, g, b and a given \"rgba(1,1,1,1)\"", function () {
const rgba = parseColor("rgba(1,1,1,1)");
expect(rgba).to.have.ordered.members([1, 1, 1, 1]);
});
it("returns 0, 1, 0 and 1 for r, g, b and a given \"#00010001\"", function () {
const rgb = parseColor("#00010001");
expect(rgb).to.have.ordered.members([0, 1, 0, 1]);
});
it("returns 1, 0, 1 and 255 for r, g, b and a given \"#10001\"", function () {
const rgb = parseColor("#10001");
expect(rgb).to.have.ordered.members([1, 0, 1, 255]);
});
it("returns 1, 1, 1 and 255 for r, g, b and a given \"rgb(1,1,1)\"", function () {
const rgba = parseColor("rgb(1,1,1)");
expect(rgba).to.have.ordered.members([1, 1, 1, 255]);
});
});
});