Commit 45331c7d authored by Mark Stenglein's avatar Mark Stenglein Committed by GitHub
Browse files

Merge pull request #41 from ocelotsloth/5-fmt-type

Parameter: FormatType: Adds FormatType and tests

Closes #5 
parents 267f1396 a5b4577b
Pipeline #1237 passed with stage
in 42 seconds
/*
* lib-ical
* Copyright (C) 2017 Mark Stenglein
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Parameter from "./Parameter";
/**
* Parameter Name: FMTTYPE
*
* Purpose: To specify the content type of a referenced object.
*
* Format Definition: This property parameter is defined by the
* following notation:
*
* fmttypeparam = "FMTTYPE" "=" type-name "/" subtype-name
* ; Where "type-name" and "subtype-name" are
* ; defined in Section 4.2 of [RFC4288].
*
* Type and subtype names MUST conform to the following ABNF:
*
* type-name = reg-name
* subtype-name = reg-name
*
* reg-name = 1*127reg-name-chars
* reg-name-chars = ALPHA / DIGIT / "!" /
* "#" / "$" / "&" / "." /
* "+" / "-" / "^" / "_"
*
* Description: This parameter can be specified on properties that are
* used to reference an object. The parameter specifies the media
* type [RFC4288] of the referenced object. For example, on the
* "ATTACH" property, an FTP type URI value does not, by itself,
* necessarily convey the type of content associated with the
* resource. The parameter value MUST be the text for either an
* IANA-registered media type or a non-standard media type.
*
* Example:
*
* ATTACH;FMTTYPE=application/msword:ftp://example.com/pub/docs/
* agenda.doc
*
* @class
* @extends Parameter
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
export default class FormatType extends Parameter {
/** @private */
private _primaryTypeName: string;
/** @private */
private _subTypeName: string;
/**
* Builds the new format type parameter.
*
* @param typeName The full typeName: "primary/sub"
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
constructor(typeName: string) {
super("FMTTYPE", []);
this.typeName = typeName;
}
/**
* @return {string} the full typeName constructed from the primary and sub
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
get typeName(): string {
return `${this._primaryTypeName}/${this._subTypeName}`;
}
/**
* Sets both the primary and sub-name by decomposing the full name.
*
* @param {string} typeName The full typename (eg. "primary/sub")
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
set typeName(typeName: string) {
const [primary , sub, ...extra]: string[] = typeName.split("/");
if (extra.length > 0) {
throw new TypeError("Too many \"/\" characters");
}
this.primaryTypeName = primary;
this.subTypeName = sub;
this.paramValues = [typeName];
}
/**
* @return {string} just the Primary Type Name
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
get primaryTypeName(): string {
return this._primaryTypeName;
}
/**
* Validates and sets the Primary Type Name
*
* @param {string} primaryName The primary name to be set
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
set primaryTypeName(primaryName: string) {
if (!FormatType.isRegName(primaryName)) {
throw new TypeError("Invalid Primary Name");
}
else {
this._primaryTypeName = primaryName;
}
}
/**
* @return {string} just the Sub-Type Name
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
get subTypeName(): string {
return this._subTypeName;
}
/**
* Validates and sets the Sub-Type Name
*
* @param {string} subName The sub-name to be set
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
*/
set subTypeName(subName: string) {
if (!FormatType.isRegName(subName)) {
throw new TypeError("Invalid Sub-Name");
}
else {
this._subTypeName = subName;
}
}
/**
* Tests to ensure the input string is a valid RegName according to the
* ABNF specified in Section 4.2 in RFC4288
*
* type-name = reg-name
* subtype-name = reg-name
*
* reg-name = 1*127reg-name-chars
* reg-name-chars = ALPHA / DIGIT / "!" /
* "#" / "$" / "&" / "." /
* "+" / "-" / "^" / "_"
*
* - TODO: Move this to a proper validation class/module (see #40)
* - TODO: Split this into two tests, for reg-name and reg-name-chars
*
* @author Mark Stenglein <mark@stengle.in>
* @since 0.1.0
* @param input The string to be tested
* @returns bool Whether or not the string is a valid reg-name-char set
* @public
* @static
*/
static isRegName(input: string) {
return /^[a-zA-Z0-9!#$&.+\-\^_]{1,127}$/.test(input);
}
}
/*
* lib-ical
* Copyright (C) 2017 Mark Stenglein
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import FormatType from "../../src/Parameter/FormatType";
import { expect } from "chai";
import "mocha";
/**
* Test the FormatType class.
*/
describe("FormatType:", () => {
it("Exists", () => {
expect(FormatType).to.not.be.null;
});
describe("Constructor:", () => {
it("Creates an object", () => {
expect(new FormatType("test/form")).to.not.be.null;
});
it("Sets the correct Parameter Name", () => {
const param = new FormatType("test/form");
const expected = "FMTTYPE";
expect(param.paramName).to.equal(expected);
});
});
describe("Get/Set Methods:", () => {
describe("typeName:", () => {
describe("Get:", () => {
it("Constructs the correct string format", () => {
const testValue = "test/form";
const param = new FormatType(testValue);
expect(param.typeName).to.equal(testValue);
});
});
describe("Set:", () => {
it("Throws error on multiple \"/\" chars", () => {
expect(() => {
const testValue = new FormatType("too/many/splits");
}).to.throw("Too many \"/\" characters");
});
});
});
describe("primaryTypeName:", () => {
describe("Get:", () => {
it("Returns the correct value", () => {
const testValue = "Primary";
const param = new FormatType(`${testValue}/sub`);
expect(param.primaryTypeName).to.equal(testValue);
});
});
describe("Set:", () => {
// More complex behavior will be tested on the regex
it("Throws an error on incorrect value", () => {
expect(() => {
const testValue = new FormatType("incor<ct/primary");
}).to.throw("Invalid Primary Name");
});
});
});
describe("subTypeName:", () => {
describe("Get:", () => {
it("Returns the correct value", () => {
const testValue = "sub";
const param = new FormatType(`Primary/${testValue}`);
expect(param.subTypeName).to.equal(testValue);
});
});
describe("Set:", () => {
// More complex behavior will be tested on the regex
it("Throws an error on incorrect value", () => {
expect(() => {
const testValue = new FormatType("sub/incor<ct");
}).to.throw("Invalid Sub-Name");
});
});
});
});
describe("Static Methods", () => {
describe("isregName:", () => {
it("Passes Alpha", () => {
expect(FormatType.isRegName("AlPhA")).to.be.true;
});
it("Passes Digits", () => {
expect(FormatType.isRegName("0123456789")).to.be.true;
});
it("Passes !", () => {
expect(FormatType.isRegName("!")).to.be.true;
});
it("Passes #", () => {
expect(FormatType.isRegName("#")).to.be.true;
});
it("Passes $", () => {
expect(FormatType.isRegName("$")).to.be.true;
});
it("Passes &", () => {
expect(FormatType.isRegName("&")).to.be.true;
});
it("Passes .", () => {
expect(FormatType.isRegName(".")).to.be.true;
});
it("Passes +", () => {
expect(FormatType.isRegName("+")).to.be.true;
});
it("Passes -", () => {
expect(FormatType.isRegName("-")).to.be.true;
});
it("Passes ^", () => {
expect(FormatType.isRegName("^")).to.be.true;
});
it("Passes _", () => {
expect(FormatType.isRegName("_")).to.be.true;
});
it("Passes All types at once", () => {
expect(FormatType.isRegName("Aa0123456789!#$&.+-^_")).to.be.true;
});
it("Passes 127 Characters", () => {
const testValue: string = "CPbCdgIJzLgSrrZOKJDofOuIFhxgbnccXB"
+ "pGrwPZcaQxDNLtHqWKopzsJaTAzCnOKHhlxQBOKkObQjcPHXalKBNn"
+ "gnXwZNGiXsWpBbgFMYgnseHiJemJxpZhhfPBmHU";
expect(FormatType.isRegName(testValue)).to.be.true;
});
it("Fails 0 characters", () => {
expect(FormatType.isRegName("")).to.be.false;
});
it("Fails 128 characters", () => {
const testValue: string = "CPbCdgIJzLgSrrZOKJDofOuIFhxgbnccXB"
+ "pGrwPZcaQxDNLtHqWKopzsJaTAzCnOKHhlxQBOKkObQjcPHXalKBNn"
+ "gnXwZNGiXsWpBbgFMYgnseHiJemJxpZhhfPBmHUA";
expect(FormatType.isRegName(testValue)).to.be.false;
});
it("Fails <", () => {
expect(FormatType.isRegName("<")).to.be.false;
});
it("Fails /", () => {
expect(FormatType.isRegName("/")).to.be.false;
});
});
});
describe("Generator", () => {
it("Generates the correct output", () => {
const param = new FormatType("text/plain");
const expected = "FMTTYPE=text/plain";
expect(param.generate()).to.equal(expected);
});
});
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment