diff --git a/lib/rules/attribute-hyphenation.js b/lib/rules/attribute-hyphenation.js index 9873d3657..e52d321b8 100644 --- a/lib/rules/attribute-hyphenation.js +++ b/lib/rules/attribute-hyphenation.js @@ -56,7 +56,7 @@ module.exports = { ignoredAttributes = ignoredAttributes.concat(optionsPayload.ignore) } - const caseConverter = casing.getConverter(useHyphenated ? 'kebab-case' : 'camelCase') + const caseConverter = casing.getExactConverter(useHyphenated ? 'kebab-case' : 'camelCase') function reportIssue (node, name) { const text = sourceCode.getText(node.key) diff --git a/lib/rules/component-definition-name-casing.js b/lib/rules/component-definition-name-casing.js index 350ea9066..9012873a4 100644 --- a/lib/rules/component-definition-name-casing.js +++ b/lib/rules/component-definition-name-casing.js @@ -48,8 +48,7 @@ module.exports = { range = node.range } - const value = casing.getConverter(caseType)(nodeValue) - if (value !== nodeValue) { + if (!casing.getChecker(caseType)(nodeValue)) { context.report({ node: node, message: 'Property name "{{value}}" is not {{caseType}}.', @@ -57,7 +56,7 @@ module.exports = { value: nodeValue, caseType: caseType }, - fix: fixer => fixer.replaceTextRange([range[0] + 1, range[1] - 1], value) + fix: fixer => fixer.replaceTextRange([range[0] + 1, range[1] - 1], casing.getExactConverter(caseType)(nodeValue)) }) } } diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js index 2a8970555..91481b0f4 100644 --- a/lib/rules/component-name-in-template-casing.js +++ b/lib/rules/component-name-in-template-casing.js @@ -87,7 +87,7 @@ module.exports = { } // We only verify the components registered in the component. if (registeredComponents - .filter(name => casing.pascalCase(name) === name) // When defining a component with PascalCase, you can use either case + .filter(name => casing.isPascalCase(name)) // When defining a component with PascalCase, you can use either case .some(name => node.rawName === name || casing.pascalCase(node.rawName) === name)) { return true } @@ -108,11 +108,10 @@ module.exports = { } const name = node.rawName - const casingName = casing.getConverter(caseType)(name) - if (casingName !== name) { + if (!casing.getChecker(caseType)(name)) { const startTag = node.startTag const open = tokens.getFirstToken(startTag) - + const casingName = casing.getExactConverter(caseType)(name) context.report({ node: open, loc: open.loc, diff --git a/lib/rules/name-property-casing.js b/lib/rules/name-property-casing.js index 041967cf0..26ae6bfa7 100644 --- a/lib/rules/name-property-casing.js +++ b/lib/rules/name-property-casing.js @@ -48,8 +48,8 @@ module.exports = { if (!node) return - const value = casing.getConverter(caseType)(node.value.value) - if (value !== node.value.value) { + if (!casing.getChecker(caseType)(node.value.value)) { + const value = casing.getExactConverter(caseType)(node.value.value) context.report({ node: node.value, message: 'Property name "{{value}}" is not {{caseType}}.', diff --git a/lib/rules/no-unregistered-components.js b/lib/rules/no-unregistered-components.js index f387248ac..6ee325058 100644 --- a/lib/rules/no-unregistered-components.js +++ b/lib/rules/no-unregistered-components.js @@ -128,8 +128,7 @@ module.exports = { // Component registered as `foo-bar` cannot be used as `FooBar` if ( - name.indexOf('-') === -1 && - name === casing.pascalCase(name) && + casing.isPascalCase(name) && componentsRegisteredAsKebabCase.indexOf(kebabCaseName) !== -1 ) { return true diff --git a/lib/rules/no-unused-components.js b/lib/rules/no-unused-components.js index 7f3920d20..21d2233cc 100644 --- a/lib/rules/no-unused-components.js +++ b/lib/rules/no-unused-components.js @@ -87,7 +87,7 @@ module.exports = { // it can be used in various of ways inside template, // like "theComponent", "The-component" etc. // but except snake_case - if (casing.pascalCase(name) === name || casing.camelCase(name) === name) { + if (casing.isPascalCase(name) || casing.isCamelCase(name)) { return ![...usedComponents].some(n => { return n.indexOf('_') === -1 && (name === casing.pascalCase(n) || casing.camelCase(n) === name) }) diff --git a/lib/rules/prop-name-casing.js b/lib/rules/prop-name-casing.js index 235dda23c..532b41ce7 100644 --- a/lib/rules/prop-name-casing.js +++ b/lib/rules/prop-name-casing.js @@ -15,7 +15,7 @@ const allowedCaseOptions = ['camelCase', 'snake_case'] function create (context) { const options = context.options[0] const caseType = allowedCaseOptions.indexOf(options) !== -1 ? options : 'camelCase' - const converter = casing.getConverter(caseType) + const checker = casing.getChecker(caseType) // ---------------------------------------------------------------------- // Public @@ -31,8 +31,7 @@ function create (context) { // (boolean | null | number | RegExp) Literal continue } - const convertedName = converter(propName) - if (convertedName !== propName) { + if (!checker(propName)) { context.report({ node: item.node, message: 'Prop "{{name}}" is not in {{caseType}}.', diff --git a/lib/utils/casing.js b/lib/utils/casing.js index 741481ef8..db01bc2e6 100644 --- a/lib/utils/casing.js +++ b/lib/utils/casing.js @@ -1,6 +1,27 @@ const assert = require('assert') -const invalidChars = /[^a-zA-Z0-9:]+/g +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * Capitalize a string. + */ +function capitalize (str) { + return str.charAt(0).toUpperCase() + str.slice(1) +} +/** + * Checks whether the given string has symbols. + */ +function hasSymbols (str) { + return /[!"#%&'()*+,./:;<=>?@[\\\]^`{|}]/u.exec(str) // without " ", "$", "-" and "_" +} +/** + * Checks whether the given string has upper. + */ +function hasUpper (str) { + return /[A-Z]/u.exec(str) +} /** * Convert text to kebab-case @@ -9,13 +30,26 @@ const invalidChars = /[^a-zA-Z0-9:]+/g */ function kebabCase (str) { return str - .replace(/[A-Z]/g, match => '-' + match) - .replace(/([^a-zA-Z])-([A-Z])/g, match => match[0] + match[2]) - .replace(/^-/, '') - .replace(invalidChars, '-') + .replace(/_/gu, '-') + .replace(/\B([A-Z])/gu, '-$1') .toLowerCase() } +/** + * Checks whether the given string is kebab-case. + */ +function isKebabCase (str) { + if ( + hasUpper(str) || + hasSymbols(str) || + /^-/u.exec(str) || // starts with hyphen is not kebab-case + /_|--|\s/u.exec(str) + ) { + return false + } + return true +} + /** * Convert text to snake_case * @param {string} str Text to be converted @@ -23,25 +57,49 @@ function kebabCase (str) { */ function snakeCase (str) { return str - .replace(/[A-Z]/g, match => '_' + match) - .replace(/([^a-zA-Z])_([A-Z])/g, match => match[0] + match[2]) - .replace(/^_/, '') - .replace(invalidChars, '_') + .replace(/\B([A-Z])/gu, '_$1') + .replace(/-/gu, '_') .toLowerCase() } +/** + * Checks whether the given string is snake_case. + */ +function isSnakeCase (str) { + if ( + hasUpper(str) || + hasSymbols(str) || + /-|__|\s/u.exec(str) + ) { + return false + } + return true +} + /** * Convert text to camelCase * @param {string} str Text to be converted * @return {string} Converted string */ function camelCase (str) { - return str - .replace(/_/g, (_, index) => index === 0 ? _ : '-') - .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => - index === 0 ? letter.toLowerCase() : letter.toUpperCase() - ) - .replace(invalidChars, '') + if (isPascalCase(str)) { + return str.charAt(0).toLowerCase() + str.slice(1) + } + return str.replace(/[-_](\w)/gu, (_, c) => c ? c.toUpperCase() : '') +} + +/** + * Checks whether the given string is camelCase. + */ +function isCamelCase (str) { + if ( + hasSymbols(str) || + /^[A-Z]/u.exec(str) || + /-|_|\s/u.exec(str) // kebab or snake or space + ) { + return false + } + return true } /** @@ -50,10 +108,21 @@ function camelCase (str) { * @return {string} Converted string */ function pascalCase (str) { - return str - .replace(/_/g, (_, index) => index === 0 ? _ : '-') - .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => letter.toUpperCase()) - .replace(invalidChars, '') + return capitalize(camelCase(str)) +} + +/** + * Checks whether the given string is PascalCase. + */ +function isPascalCase (str) { + if ( + hasSymbols(str) || + /^[a-z]/u.exec(str) || + /-|_|\s/u.exec(str) // kebab or snake or space + ) { + return false + } + return true } const convertersMap = { @@ -63,6 +132,34 @@ const convertersMap = { 'PascalCase': pascalCase } +const checkersMap = { + 'kebab-case': isKebabCase, + 'snake_case': isSnakeCase, + 'camelCase': isCamelCase, + 'PascalCase': isPascalCase +} +/** +* Return case checker +* @param {string} name type of checker to return ('camelCase', 'kebab-case', 'PascalCase') +* @return {isKebabCase|isCamelCase|isPascalCase} +*/ +function getChecker (name) { + assert(typeof name === 'string') + + return checkersMap[name] || isPascalCase +} + +/** + * Return case converter + * @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {kebabCase|camelCase|pascalCase} + */ +function getConverter (name) { + assert(typeof name === 'string') + + return convertersMap[name] || pascalCase +} + module.exports = { allowedCaseOptions: [ 'camelCase', @@ -75,14 +172,37 @@ module.exports = { * @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') * @return {kebabCase|camelCase|pascalCase} */ - getConverter (name) { - assert(typeof name === 'string') + getConverter, + + /** + * Return case checker + * @param {string} name type of checker to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {isKebabCase|isCamelCase|isPascalCase} + */ + getChecker, - return convertersMap[name] || pascalCase + /** + * Return case exact converter. + * If the converted result is not the correct case, the original value is returned. + * @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {kebabCase|camelCase|pascalCase} + */ + getExactConverter (name) { + const converter = getConverter(name) + const checker = getChecker(name) + return (str) => { + const result = converter(str) + return checker(result) ? result : str/* cannot convert */ + } }, camelCase, pascalCase, kebabCase, - snakeCase + snakeCase, + + isCamelCase, + isPascalCase, + isKebabCase, + isSnakeCase } diff --git a/tests/lib/rules/component-definition-name-casing.js b/tests/lib/rules/component-definition-name-casing.js index d14eedb7a..2317ea523 100644 --- a/tests/lib/rules/component-definition-name-casing.js +++ b/tests/lib/rules/component-definition-name-casing.js @@ -184,11 +184,7 @@ ruleTester.run('component-definition-name-casing', rule, { name: 'foo bar' } `, - output: ` - export default { - name: 'FooBar' - } - `, + output: null, parserOptions, errors: [{ message: 'Property name "foo bar" is not PascalCase.', @@ -203,11 +199,7 @@ ruleTester.run('component-definition-name-casing', rule, { name: 'foo!bar' } `, - output: ` - export default { - name: 'FooBar' - } - `, + output: null, parserOptions, errors: [{ message: 'Property name "foo!bar" is not PascalCase.', @@ -222,11 +214,7 @@ ruleTester.run('component-definition-name-casing', rule, { name: 'foo!bar' }) `, - output: ` - new Vue({ - name: 'FooBar' - }) - `, + output: null, parserOptions: { ecmaVersion: 6 }, errors: [{ message: 'Property name "foo!bar" is not PascalCase.', diff --git a/tests/lib/rules/component-name-in-template-casing.js b/tests/lib/rules/component-name-in-template-casing.js index c1caf2e6d..d468dce84 100644 --- a/tests/lib/rules/component-name-in-template-casing.js +++ b/tests/lib/rules/component-name-in-template-casing.js @@ -686,6 +686,31 @@ tester.run('component-name-in-template-casing', rule, { 'Component name "the-component" is not PascalCase.', 'Component name "the-component" is not PascalCase.' ] + }, + { + code: ` + `, + output: ` + `, + options: ['kebab-case', { + registeredComponentsOnly: false + }], + errors: [ + 'Component name "foo--bar" is not kebab-case.', + 'Component name "Foo--Bar" is not kebab-case.', + 'Component name "FooBar" is not kebab-case.', + 'Component name "FooBar_Baz-qux" is not kebab-case.' + ] } ] }) diff --git a/tests/lib/rules/name-property-casing.js b/tests/lib/rules/name-property-casing.js index 1bdeb8769..ae2ed7dec 100644 --- a/tests/lib/rules/name-property-casing.js +++ b/tests/lib/rules/name-property-casing.js @@ -99,11 +99,7 @@ ruleTester.run('name-property-casing', rule, { name: 'foo bar' } `, - output: ` - export default { - name: 'FooBar' - } - `, + output: null, parserOptions, errors: [{ message: 'Property name "foo bar" is not PascalCase.', @@ -118,11 +114,7 @@ ruleTester.run('name-property-casing', rule, { name: 'foo!bar' } `, - output: ` - export default { - name: 'FooBar' - } - `, + output: null, parserOptions, errors: [{ message: 'Property name "foo!bar" is not PascalCase.', @@ -137,11 +129,7 @@ ruleTester.run('name-property-casing', rule, { name: 'foo!bar' }) `, - output: ` - new Vue({ - name: 'FooBar' - }) - `, + output: null, parserOptions: { ecmaVersion: 6 }, errors: [{ message: 'Property name "foo!bar" is not PascalCase.', diff --git a/tests/lib/rules/prop-name-casing.js b/tests/lib/rules/prop-name-casing.js index d80f7ab09..5290ec07d 100644 --- a/tests/lib/rules/prop-name-casing.js +++ b/tests/lib/rules/prop-name-casing.js @@ -287,6 +287,66 @@ ruleTester.run('prop-name-casing', rule, { } `, parserOptions + }, + { + // Japanese characters + filename: 'test.vue', + code: ` + export default { + props: { + '漢字': String + } + } + `, + parserOptions + }, + { + // emoji + filename: 'test.vue', + code: ` + export default { + props: { + '\u{1F37B}': String + } + } + `, + parserOptions + }, + { + // #862 + filename: 'test.vue', + code: ` + export default { + props: { + $actionEl: String + } + } + `, + parserOptions + }, + { + // #932 + filename: 'test.vue', + code: ` + export default { + props: { + $css: String + } + } + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + props: { + _item: String + } + } + `, + options: ['snake_case'], + parserOptions } ], @@ -441,35 +501,34 @@ ruleTester.run('prop-name-casing', rule, { }] }, { - // emoji filename: 'test.vue', code: ` export default { props: { - '\u{1F37B}': String + 'abc-123-def': String } } `, parserOptions, errors: [{ - message: 'Prop "\u{1F37B}" is not in camelCase.', + message: 'Prop "abc-123-def" is not in camelCase.', type: 'Property', line: 4 }] }, { - // Japanese characters + // Parentheses computed property name filename: 'test.vue', code: ` export default { props: { - '漢字': String + [('greeting-text')]: String } } `, parserOptions, errors: [{ - message: 'Prop "漢字" is not in camelCase.', + message: 'Prop "greeting-text" is not in camelCase.', type: 'Property', line: 4 }] @@ -479,33 +538,26 @@ ruleTester.run('prop-name-casing', rule, { code: ` export default { props: { - 'abc-123-def': String + _item: String } } `, parserOptions, - errors: [{ - message: 'Prop "abc-123-def" is not in camelCase.', - type: 'Property', - line: 4 - }] + errors: ['Prop "_item" is not in camelCase.'] }, { - // Parentheses computed property name filename: 'test.vue', code: ` export default { props: { - [('greeting-text')]: String + _itemName: String } } `, + options: ['snake_case'], parserOptions, - errors: [{ - message: 'Prop "greeting-text" is not in camelCase.', - type: 'Property', - line: 4 - }] + errors: ['Prop "_itemName" is not in snake_case.'] } + ] }) diff --git a/tests/lib/utils/casing.js b/tests/lib/utils/casing.js index d72d186d5..28a71ce53 100644 --- a/tests/lib/utils/casing.js +++ b/tests/lib/utils/casing.js @@ -9,6 +9,7 @@ describe('getConverter()', () => { it('should convert string to camelCase', () => { const converter = casing.getConverter('camelCase') + assert.equal(converter('foo'), 'foo') assert.equal(converter('fooBar'), 'fooBar') assert.equal(converter('foo-bar'), 'fooBar') assert.equal(converter('foo_bar'), 'fooBar') @@ -17,11 +18,18 @@ describe('getConverter()', () => { assert.equal(converter('FooBAR'), 'fooBAR') assert.equal(converter('Foo1BAZ'), 'foo1BAZ') assert.equal(converter('foo1b_a_z'), 'foo1bAZ') + assert.equal(converter('darИībaÊÊw'), 'darИībaÊÊw') + assert.equal(converter('klâwen-ûf'), 'klâwen-ûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхИвдалП') + assert.equal(converter('kpłĄżć'), 'kpłĄżć') + assert.equal(converter('ÊtreSîne'), 'êtreSîne') + assert.equal(converter(' foo Bar '), ' foo Bar ') }) it('should convert string to PascalCase', () => { const converter = casing.getConverter('PascalCase') + assert.equal(converter('foo'), 'Foo') assert.equal(converter('fooBar'), 'FooBar') assert.equal(converter('foo-bar'), 'FooBar') assert.equal(converter('foo_bar'), 'FooBar') @@ -30,30 +38,184 @@ describe('getConverter()', () => { assert.equal(converter('FooBAR'), 'FooBAR') assert.equal(converter('Foo1BAZ'), 'Foo1BAZ') assert.equal(converter('foo1b_a_z'), 'Foo1bAZ') + assert.equal(converter('darИībaÊÊw'), 'DarИībaÊÊw') + assert.equal(converter('klâwen-ûf'), 'Klâwen-ûf') + assert.equal(converter('пустынныхИвдалП'), 'ПустынныхИвдалП') + assert.equal(converter('kpłĄżć'), 'KpłĄżć') + assert.equal(converter('ÊtreSîne'), 'ÊtreSîne') + assert.equal(converter(' foo Bar '), ' foo Bar ') }) it('should convert string to kebab-case', () => { const converter = casing.getConverter('kebab-case') + assert.equal(converter('foo'), 'foo') assert.equal(converter('fooBar'), 'foo-bar') assert.equal(converter('foo-bar'), 'foo-bar') assert.equal(converter('foo_bar'), 'foo-bar') assert.equal(converter('FooBar'), 'foo-bar') - assert.equal(converter('Foo1Bar'), 'foo1bar') + assert.equal(converter('Foo1Bar'), 'foo1-bar') assert.equal(converter('FooBAR'), 'foo-b-a-r') - assert.equal(converter('Foo1BAZ'), 'foo1b-a-z') + assert.equal(converter('Foo1BAZ'), 'foo1-b-a-z') assert.equal(converter('foo1b_a_z'), 'foo1b-a-z') + assert.equal(converter('darИībaÊÊw'), 'darиībaêêw') + assert.equal(converter('klâwen-ûf'), 'klâwen-ûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхивдалп') + assert.equal(converter('kpłĄżć'), 'kpłążć') + assert.equal(converter('ÊtreSîne'), 'être-sîne') + assert.equal(converter(' foo Bar '), ' foo bar ') }) it('should convert string to snake_case', () => { const converter = casing.getConverter('snake_case') + assert.equal(converter('a'), 'a') assert.equal(converter('fooBar'), 'foo_bar') assert.equal(converter('foo-bar'), 'foo_bar') assert.equal(converter('FooBar'), 'foo_bar') - assert.equal(converter('Foo1Bar'), 'foo1bar') + assert.equal(converter('Foo1Bar'), 'foo1_bar') assert.equal(converter('FooBAR'), 'foo_b_a_r') - assert.equal(converter('Foo1BAZ'), 'foo1b_a_z') + assert.equal(converter('Foo1BAZ'), 'foo1_b_a_z') assert.equal(converter('foo1b_a_z'), 'foo1b_a_z') + assert.equal(converter('darИībaÊÊw'), 'darиībaêêw') + assert.equal(converter('klâwen-ûf'), 'klâwen_ûf') + assert.equal(converter('пустынныхИвдалП'), 'пустынныхивдалп') + assert.equal(converter('kpłĄżć'), 'kpłążć') + assert.equal(converter('ÊtreSîne'), 'être_sîne') + assert.equal(converter(' foo Bar '), ' foo bar ') + }) +}) + +describe('getChecker()', () => { + it('should check string to camelCase', () => { + const checker = casing.getChecker('camelCase') + + assert.equal(checker('foo'), true) + assert.equal(checker('fooBar'), true) + assert.equal(checker('fooBar'), true) + assert.equal(checker('fooBar'), true) + assert.equal(checker('fooBar'), true) + assert.equal(checker('foo1Bar'), true) + assert.equal(checker('fooBAR'), true) + assert.equal(checker('foo1BAZ'), true) + assert.equal(checker('foo1bAZ'), true) + assert.equal(checker('darИībaÊÊw'), true) + assert.equal(checker('klâwen-ûf'), false) + assert.equal(checker('пустынныхИвдалП'), true) + assert.equal(checker('kpłĄżć'), true) + assert.equal(checker('êtreSîne'), true) + assert.equal(checker(' foo Bar '), false) + + assert.equal(checker('camelCase'), true) + assert.equal(checker('PascalCase'), false) + assert.equal(checker('kebab-case'), false) + assert.equal(checker('snake_case'), false) + + assert.equal(checker('camel-Kebab-Case'), false) + assert.equal(checker('camel_Snake_Case'), false) + assert.equal(checker('Pascal-Kebab-Case'), false) + assert.equal(checker('Pascal_Snake_Case'), false) + assert.equal(checker('snake_kebab-case'), false) + }) + + it('should check string to PascalCase', () => { + const checker = casing.getChecker('PascalCase') + + assert.equal(checker('Foo'), true) + assert.equal(checker('FooBar'), true) + assert.equal(checker('FooBar'), true) + assert.equal(checker('FooBar'), true) + assert.equal(checker('FooBar'), true) + assert.equal(checker('Foo1Bar'), true) + assert.equal(checker('FooBAR'), true) + assert.equal(checker('Foo1BAZ'), true) + assert.equal(checker('Foo1bAZ'), true) + assert.equal(checker('DarИībaÊÊw'), true) + assert.equal(checker('Klâwen-ûf'), false) + assert.equal(checker('ПустынныхИвдалП'), true) + assert.equal(checker('KpłĄżć'), true) + assert.equal(checker('ÊtreSîne'), true) + assert.equal(checker(' foo Bar '), false) + + assert.equal(checker('DarbībaShow'), true) + + assert.equal(checker('camelCase'), false) + assert.equal(checker('PascalCase'), true) + assert.equal(checker('kebab-case'), false) + assert.equal(checker('snake_case'), false) + + assert.equal(checker('camel-Kebab-Case'), false) + assert.equal(checker('camel_Snake_Case'), false) + assert.equal(checker('Pascal-Kebab-Case'), false) + assert.equal(checker('Pascal_Snake_Case'), false) + assert.equal(checker('snake_kebab-case'), false) + }) + + it('should convert string to kebab-case', () => { + const checker = casing.getChecker('kebab-case') + + assert.equal(checker('foo'), true) + assert.equal(checker('foo-bar'), true) + assert.equal(checker('foo-bar'), true) + assert.equal(checker('foo-bar'), true) + assert.equal(checker('foo-bar'), true) + assert.equal(checker('foo1-bar'), true) + assert.equal(checker('foo-b-a-r'), true) + assert.equal(checker('foo1-b-a-z'), true) + assert.equal(checker('foo1b-a-z'), true) + assert.equal(checker('darиībaêêw'), true) + assert.equal(checker('klâwen-ûf'), true) + assert.equal(checker('пустынныхивдалп'), true) + assert.equal(checker('kpłążć'), true) + assert.equal(checker('être-sîne'), true) + assert.equal(checker(' foo bar '), false) + + assert.equal(checker('camelCase'), false) + assert.equal(checker('PascalCase'), false) + assert.equal(checker('kebab-case'), true) + assert.equal(checker('snake_case'), false) + + assert.equal(checker('camel-Kebab-Case'), false) + assert.equal(checker('camel_Snake_Case'), false) + assert.equal(checker('Pascal-Kebab-Case'), false) + assert.equal(checker('Pascal_Snake_Case'), false) + assert.equal(checker('snake_kebab-case'), false) + + assert.equal(checker('valid-kebab-case'), true) + assert.equal(checker('-invalid-kebab-case'), false) + assert.equal(checker('invalid--kebab-case'), false) + }) + + it('should check string to snake_case', () => { + const checker = casing.getChecker('snake_case') + + assert.equal(checker('a'), true) + assert.equal(checker('foo_bar'), true) + assert.equal(checker('foo_bar'), true) + assert.equal(checker('foo_bar'), true) + assert.equal(checker('foo1_bar'), true) + assert.equal(checker('foo_b_a_r'), true) + assert.equal(checker('foo1_b_a_z'), true) + assert.equal(checker('foo1b_a_z'), true) + assert.equal(checker('darиībaêêw'), true) + assert.equal(checker('klâwen_ûf'), true) + assert.equal(checker('пустынныхивдалп'), true) + assert.equal(checker('kpłążć'), true) + assert.equal(checker('être_sîne'), true) + assert.equal(checker(' foo bar '), false) + + assert.equal(checker('camelCase'), false) + assert.equal(checker('PascalCase'), false) + assert.equal(checker('kebab-case'), false) + assert.equal(checker('snake_case'), true) + + assert.equal(checker('camel-Kebab-Case'), false) + assert.equal(checker('camel_Snake_Case'), false) + assert.equal(checker('Pascal-Kebab-Case'), false) + assert.equal(checker('Pascal_Snake_Case'), false) + assert.equal(checker('snake_kebab-case'), false) + + assert.equal(checker('_valid_snake_case'), true) + assert.equal(checker('invalid__snake_case'), false) }) })