superflected
Version:
A port of ActiveSupport's inflector to Node.js
453 lines (373 loc) • 15.7 kB
text/typescript
import { describe, it, expect } from "vitest";
import {
pluralize,
singularize,
capitalize,
titleize,
camelize,
inflections,
humanize,
underscore,
classify,
tableize,
setInflections,
foreignKey,
ordinal,
ordinalize,
dasherize,
parameterize,
constantify,
setTransliterations,
} from "../src";
const inflect = inflections();
import TestCases from "./cases";
describe("Inflector", () => {
it("properly pluralizes plurals", () => {
expect(pluralize("plurals")).toBe("plurals");
expect(pluralize("Plurals")).toBe("Plurals");
});
it("properly pluralizes empty string", () => {
expect(pluralize("")).toBe("");
});
it("properly capitalizes strings", () => {
expect(capitalize("foo")).toBe("Foo");
expect(capitalize("FOO")).toBe("FOO");
expect(capitalize("foo bar")).toBe("Foo bar");
expect(capitalize("")).toBe("");
expect(capitalize(null)).toBe("");
expect(capitalize(undefined)).toBe("");
});
for (const word of inflect.uncountables) {
it("respects the uncountability of " + word, () => {
expect(singularize(word)).toBe(word);
expect(pluralize(word)).toBe(word);
expect(singularize(word)).toBe(pluralize(word));
});
}
it("checks uncountable word is not greedy", () => {
const uncountableWord = "ors";
const countableWord = "sponsor";
inflect.uncountables.push(uncountableWord);
expect(singularize(uncountableWord)).toBe(uncountableWord);
expect(pluralize(uncountableWord)).toBe(uncountableWord);
expect(singularize(uncountableWord)).toBe(pluralize(uncountableWord));
expect(singularize(countableWord)).toBe("sponsor");
expect(pluralize(countableWord)).toBe("sponsors");
expect(singularize(pluralize(countableWord))).toBe("sponsor");
});
for (const [singular, plural] of Object.entries(TestCases.SingularToPlural)) {
it("properly pluralizes " + singular, () => {
expect(pluralize(singular)).toBe(plural);
expect(pluralize(capitalize(singular))).toBe(capitalize(plural));
});
it("properly pluralizes " + plural, () => {
expect(pluralize(plural)).toBe(plural);
expect(pluralize(capitalize(plural))).toBe(capitalize(plural));
});
it("properly singularizes " + plural, () => {
expect(singularize(plural)).toBe(singular);
expect(singularize(capitalize(plural))).toBe(capitalize(singular));
});
it("properly singularizes " + singular, () => {
expect(singularize(singular)).toBe(singular);
expect(singularize(capitalize(singular))).toBe(capitalize(singular));
});
}
it("allows overwriting defined inflectors", () => {
expect(singularize("series")).toBe("series");
inflect.singular("series", "serie");
expect(singularize("series")).toBe("serie");
});
for (const [mixture, titleized] of Object.entries(TestCases.MixtureToTitleCase)) {
it("properly titleizes " + mixture, () => {
expect(titleize(mixture)).toBe(titleized);
});
}
for (const [camel, underscore] of Object.entries(TestCases.CamelToUnderscore)) {
it("properly camelizes " + underscore, () => {
expect(camelize(underscore)).toBe(camel);
});
}
it("properly camelizes with lower downcases the first letter", () => {
expect(camelize("Capital", false)).toBe("capital");
});
it("properly camelizes with underscores", () => {
expect(camelize("Camel_Case")).toBe("CamelCase");
});
it("properly handles acronyms", () => {
inflect.acronym("API");
inflect.acronym("HTML");
inflect.acronym("HTTP");
inflect.acronym("RESTful");
inflect.acronym("W3C");
inflect.acronym("PhD");
inflect.acronym("RoR");
inflect.acronym("SSL");
// camelize underscore humanize titleize
const items = [
["API", "api", "API", "API"],
["APIController", "api_controller", "API controller", "API Controller"],
["Nokogiri/HTML", "nokogiri/html", "Nokogiri/HTML", "Nokogiri/HTML"],
["HTTPAPI", "http_api", "HTTP API", "HTTP API"],
["HTTP/Get", "http/get", "HTTP/get", "HTTP/Get"],
["SSLError", "ssl_error", "SSL error", "SSL Error"],
["RESTful", "restful", "RESTful", "RESTful"],
["RESTfulController", "restful_controller", "RESTful controller", "RESTful Controller"],
["IHeartW3C", "i_heart_w3c", "I heart W3C", "I Heart W3C"],
["PhDRequired", "phd_required", "PhD required", "PhD Required"],
["IRoRU", "i_ror_u", "I RoR u", "I RoR U"],
["RESTfulHTTPAPI", "restful_http_api", "RESTful HTTP API", "RESTful HTTP API"],
// misdirection
["Capistrano", "capistrano", "Capistrano", "Capistrano"],
["CapiController", "capi_controller", "Capi controller", "Capi Controller"],
["HttpsApis", "https_apis", "Https apis", "Https Apis"],
["Html5", "html5", "Html5", "Html5"],
["Restfully", "restfully", "Restfully", "Restfully"],
["RoRails", "ro_rails", "Ro rails", "Ro Rails"],
];
for (const [camel, under, human, title] of items) {
expect(camelize(under)).toBe(camel);
expect(camelize(camel)).toBe(camel);
expect(underscore(under)).toBe(under);
expect(underscore(camel)).toBe(under);
expect(titleize(under)).toBe(title);
expect(titleize(camel)).toBe(title);
expect(humanize(under)).toBe(human);
}
});
it("allows overwriting acronyms", () => {
inflect.acronym("API");
inflect.acronym("LegacyApi");
expect(camelize("legacyapi")).toBe("LegacyApi");
expect(camelize("legacy_api")).toBe("LegacyAPI");
expect(camelize("some_legacyapi")).toBe("SomeLegacyApi");
expect(camelize("nonlegacyapi")).toBe("Nonlegacyapi");
});
it("properly handles lower camelized acronyms", () => {
inflect.acronym("API");
inflect.acronym("HTML");
expect(camelize("html_api", false)).toBe("htmlAPI");
expect(camelize("htmlAPI", false)).toBe("htmlAPI");
expect(camelize("HTMLAPI", false)).toBe("htmlAPI");
});
it("properly handles lower camelized acronyms", () => {
inflect.acronym("API");
inflect.acronym("JSON");
inflect.acronym("HTML");
expect(underscore("JSONHTMLAPI")).toBe("json_html_api");
});
it("properly underscores", () => {
for (const [camel, underscored] of Object.entries(TestCases.CamelToUnderscore)) {
expect(underscore(camel)).toBe(underscored);
}
for (const [camel, underscored] of Object.entries(TestCases.CamelToUnderscoreWithoutReverse)) {
expect(underscore(camel)).toBe(underscored);
}
});
it("properly adds a foreign key suffix", () => {
for (const [klass, foreignKeyized] of Object.entries(TestCases.ClassNameToForeignKeyWithUnderscore)) {
expect(foreignKey(klass)).toBe(foreignKeyized);
}
for (const [klass, foreignKeyized] of Object.entries(TestCases.ClassNameToForeignKeyWithoutUnderscore)) {
expect(foreignKey(klass, false)).toBe(foreignKeyized);
}
});
it("properly tableizes class names", () => {
for (const [className, tableName] of Object.entries(TestCases.ClassNameToTableName)) {
expect(tableize(className)).toBe(tableName);
}
});
it("properly classifies table names", () => {
for (const [className, tableName] of Object.entries(TestCases.ClassNameToTableName)) {
expect(classify(tableName)).toBe(className);
expect(classify("table_prefix." + tableName)).toBe(className);
}
});
it("properly classifies with leading schema name", () => {
expect(classify("schema.foo_bar")).toBe("FooBar");
});
it("properly humanizes underscored strings", () => {
for (const [underscore, human] of Object.entries(TestCases.UnderscoreToHuman)) {
expect(humanize(underscore)).toBe(human);
}
});
it("properly humanizes underscored strings without capitalize", () => {
for (const [underscore, human] of Object.entries(TestCases.UnderscoreToHumanWithoutCapitalize)) {
expect(humanize(underscore, { capitalize: false })).toBe(human);
}
});
it("properly humanizes by rule", () => {
inflect.human(/_cnt$/i, "_count");
inflect.human(/^prefx_/i, "");
expect(humanize("jargon_cnt")).toBe("Jargon count");
expect(humanize("prefx_request")).toBe("Request");
});
it("properly humanizes by string", () => {
inflect.human("col_rpted_bugs", "Reported bugs");
expect(humanize("col_rpted_bugs")).toBe("Reported bugs");
expect(humanize("COL_rpted_bugs")).toBe("Col rpted bugs");
});
it("properly generates ordinal suffixes", () => {
for (const [number, ordinalized] of Object.entries(TestCases.OrdinalNumbers)) {
expect(ordinalized).toBe(number + ordinal(number));
}
});
it("properly ordinalizes numbers", () => {
for (const [number, ordinalized] of Object.entries(TestCases.OrdinalNumbers)) {
expect(ordinalize(number)).toBe(ordinalized);
}
});
it("properly dasherizes underscored strings", () => {
for (const [underscored, dasherized] of Object.entries(TestCases.UnderscoresToDashes)) {
expect(dasherize(underscored)).toBe(dasherized);
}
});
it("properly underscores as reverse of dasherize", () => {
for (const [underscored, _dasherized] of Object.entries(TestCases.UnderscoresToDashes)) {
expect(underscore(dasherize(underscored))).toBe(underscored);
}
});
it("properly underscores to lower camel", () => {
for (const [underscored, lowerCamel] of Object.entries(TestCases.UnderscoreToLowerCamel)) {
expect(camelize(underscored, false)).toBe(lowerCamel);
}
});
it("respects the inflector locale", () => {
setInflections("es", function (inflect) {
inflect.plural(/$/, "s");
inflect.plural(/z$/i, "ces");
inflect.singular(/s$/, "");
inflect.singular(/es$/, "");
inflect.irregular("el", "los");
});
expect(pluralize("hijo", "es")).toBe("hijos");
expect(pluralize("luz", "es")).toBe("luces");
expect(pluralize("luz")).toBe("luzs");
expect(singularize("sociedades", "es")).toBe("sociedad");
expect(singularize("sociedades")).toBe("sociedade");
expect(pluralize("el", "es")).toBe("los");
expect(pluralize("el")).toBe("els");
setInflections("es", function (inflect) {
inflect.clear();
});
expect(inflections("es").plurals.length).toBe(0);
expect(inflections("es").singulars.length).toBe(0);
expect(inflections().plurals.length).not.toBe(0);
expect(inflections().singulars.length).not.toBe(0);
});
describe("pluralization", () => {
for (const [singular, plural] of Object.entries(TestCases.Irregularities)) {
it("respects the irregularity between " + singular + " and " + plural, () => {
setInflections("en", function (inflect) {
inflect.irregular(singular, plural);
});
expect(singularize(plural)).toBe(singular);
expect(pluralize(singular)).toBe(plural);
});
}
for (const [singular, plural] of Object.entries(TestCases.Irregularities)) {
it("makes sure that pluralize of irregularity " + plural + " is the same", () => {
setInflections("en", function (inflect) {
inflect.irregular(singular, plural);
});
expect(pluralize(plural)).toBe(plural);
});
}
for (const [singular, plural] of Object.entries(TestCases.Irregularities)) {
it("makes sure that singularize of irregularity " + singular + " is the same", () => {
setInflections("en", function (inflect) {
inflect.irregular(singular, plural);
});
expect(singularize(singular)).toBe(singular);
});
}
});
for (const scope of ["plurals", "singulars", "uncountables", "humans"] as const) {
it("properly clears " + scope + " inflection scope", () => {
setInflections("en", function (inflect) {
inflect.clear(scope);
});
expect(inflections("en")[scope].length).toBe(0);
});
}
it("properly clears all reflection scopes", () => {
setInflections("en", function (inflect) {
// ensure any data is present
inflect.plural(/(quiz)$/i, "$1zes");
inflect.singular(/(database)s$/i, "$1");
inflect.uncountable("series");
inflect.human("col_rpted_bugs", "Reported bugs");
inflect.clear("all");
expect(inflect.plurals.length).toBe(0);
expect(inflect.singulars.length).toBe(0);
expect(inflect.uncountables.length).toBe(0);
expect(inflect.humans.length).toBe(0);
});
});
it("properly clears with default", () => {
setInflections("es", function (inflect) {
// ensure any data is present
inflect.plural(/(quiz)$/i, "$1zes");
inflect.singular(/(database)s$/i, "$1");
inflect.uncountable("series");
inflect.human("col_rpted_bugs", "Reported bugs");
inflect.clear();
expect(inflect.plurals.length).toBe(0);
expect(inflect.singulars.length).toBe(0);
expect(inflect.uncountables.length).toBe(0);
expect(inflect.humans.length).toBe(0);
});
});
it("properly parameterizes", () => {
for (const [someString, parameterizedString] of Object.entries(TestCases.StringToParameterized)) {
expect(parameterize(someString)).toBe(parameterizedString);
}
});
it("properly parameterizes and normalizes", () => {
for (const [someString, parameterizedString] of Object.entries(TestCases.StringToParameterizedAndNormalized)) {
expect(parameterize(someString)).toBe(parameterizedString);
}
});
it("properly parameterizes with custom separator", () => {
for (const [someString, parameterizedString] of Object.entries(TestCases.StringToParameterizeWithUnderscore)) {
expect(parameterize(someString, { separator: "_" })).toBe(parameterizedString);
}
});
it("properly parameterizes with no separator", () => {
for (const [someString, parameterizedString] of Object.entries(TestCases.StringToParameterizeWithNoSeparator)) {
expect(parameterize(someString, { separator: null })).toBe(parameterizedString);
expect(parameterize(someString, { separator: "" })).toBe(parameterizedString);
}
});
it("properly parameterizes with preserve-case option", () => {
for (const [someString, parameterizedString] of Object.entries(TestCases.StringToParameterizeWithPreserveCase)) {
expect(parameterize(someString, { preserveCase: true })).toBe(parameterizedString);
}
});
it("properly parameterizes with multi character separator", () => {
for (const [someString, parameterizedString] of Object.entries(TestCases.StringToParameterized)) {
expect(parameterize(someString, { separator: "__sep__" })).toBe(parameterizedString.replace(/-/g, "__sep__"));
}
});
it("allows overwriting transliterate approximations", () => {
expect(parameterize("Jürgen")).toBe("jurgen");
setTransliterations("en", (transliterate) => {
transliterate.approximate("ü", "ue");
});
expect(parameterize("Jürgen")).toBe("juergen");
});
it("allows overwriting transliterate approximations for a specific locale", () => {
expect(parameterize("Mädchen")).toBe("madchen");
expect(parameterize("Mädchen", { locale: "de" })).toBe("madchen");
setTransliterations("de", (transliterate) => {
transliterate.approximate("ä", "ae");
});
expect(parameterize("Mädchen")).toBe("madchen");
expect(parameterize("Mädchen", { locale: "de" })).toBe("maedchen");
});
it("properly converts words to constant case", () => {
for (const [words, constantCase] of Object.entries(TestCases.WordsToConstantCase)) {
expect(constantify(words)).toBe(constantCase);
}
});
});