diff --git a/src/diagrams/er/parser/erDiagram.jison b/src/diagrams/er/parser/erDiagram.jison index ceae4f18cf..067851e3cc 100644 --- a/src/diagrams/er/parser/erDiagram.jison +++ b/src/diagrams/er/parser/erDiagram.jison @@ -24,6 +24,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili [\n]+ return 'NEWLINE'; \s+ /* skip whitespace */ [\s]+ return 'SPACE'; +\"[A-Za-z0-9\!\@\#\$\^\&\*\(\)\-_\=\+\[\]\{\};:\.\?]+\" return 'ENTITY_NAME'; \"[^"]*\" return 'WORD'; "erDiagram" return 'ER_DIAGRAM'; "{" { this.begin("block"); return 'BLOCK_START'; } @@ -102,8 +103,8 @@ statement ; entityName - : 'ALPHANUM' { $$ = $1; /*console.log('Entity: ' + $1);*/ } - | 'ALPHANUM' '.' entityName { $$ = $1 + $2 + $3; } + : 'ALPHANUM' { $$ = $1; } + | 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); } ; attributes @@ -156,6 +157,7 @@ relType role : 'WORD' { $$ = $1.replace(/"/g, ''); } + | 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); } | 'ALPHANUM' { $$ = $1; } ; diff --git a/src/diagrams/er/parser/erDiagram.spec.js b/src/diagrams/er/parser/erDiagram.spec.js index 409176f782..27d9350b73 100644 --- a/src/diagrams/er/parser/erDiagram.spec.js +++ b/src/diagrams/er/parser/erDiagram.spec.js @@ -1,6 +1,6 @@ import { setConfig } from '../../../config'; import erDb from '../erDb'; -import erDiagram from './erDiagram'; +import erDiagram from './erDiagram'; // jison file setConfig({ securityLevel: 'strict', @@ -21,14 +21,112 @@ describe('when parsing ER diagram it...', function () { expect(erDb.getRelationships().length).toBe(0); }); - it('should allow hyphens and underscores in entity names', function () { - const line1 = 'DUCK-BILLED-PLATYPUS'; - const line2 = 'CHARACTER_SET'; - erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`); - - const entities = erDb.getEntities(); - expect(entities.hasOwnProperty('DUCK-BILLED-PLATYPUS')).toBe(true); - expect(entities.hasOwnProperty('CHARACTER_SET')).toBe(true); + describe('entity name', () => { + it('cannot be empty quotes ""', function () { + const name = '""'; + expect(() => { + erDiagram.parser.parse(`erDiagram\n ${name}\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(false); + }).toThrow(); + }); + describe('has non A-Za-z0-9_- chars', function () { + const allowed = [ + '!', + '@', + '#', + '$', + '^', + '&', + '*', + '(', + ')', + '-', + '_', + '=', + '+', + '[', + ']', + '{', + '}', + ';', + ':', + '.', + '?', + ]; + + allowed.forEach((allowedChar) => { + const singleOccurrence = `Blo${allowedChar}rf`; + const repeatedOccurrence = `Blo${allowedChar}${allowedChar}rf`; + const cannontStartWith = `${allowedChar}Blorf`; + const endsWith = `Blorf${allowedChar}`; + + it(`${singleOccurrence} fails if not surrounded by quotes`, function () { + const name = singleOccurrence; + expect(() => { + erDiagram.parser.parse(`erDiagram\n ${name}\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(false); + }).toThrow(); + }); + + it(`"${singleOccurrence}" single occurrence`, function () { + const name = singleOccurrence; + erDiagram.parser.parse(`erDiagram\n "${name}"\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(true); + }); + + it(`"${repeatedOccurrence}" repeated occurrence`, function () { + const name = repeatedOccurrence; + erDiagram.parser.parse(`erDiagram\n "${name}"\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(true); + }); + + it(`"${singleOccurrence}" ends with`, function () { + const name = endsWith; + erDiagram.parser.parse(`erDiagram\n "${name}"\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(true); + }); + + it(`"${cannontStartWith}" cannot start with the character`, function () { + const name = repeatedOccurrence; + expect(() => { + erDiagram.parser.parse(`erDiagram\n "${name}"\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(false); + }).toThrow(); + }); + }); + + const allCombined = allowed.join(''); + + it(`a${allCombined} (all non-alphanumerics) in one, starting with 'a'`, function () { + const name = 'a' + allCombined; + erDiagram.parser.parse(`erDiagram\n "${name}"\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(true); + }); + }); + + it('cannot contain % because it interfers with parsing comments', function () { + expect(() => { + erDiagram.parser.parse(`erDiagram\n "Blo%rf"\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty(name)).toBe(false); + }).toThrow(); + }); + + it('can contain - _ ', function () { + const hyphens = 'DUCK-BILLED-PLATYPUS'; + const underscores = 'CHARACTER_SET'; + erDiagram.parser.parse(`erDiagram\n${hyphens}\n${underscores}\n`); + const entities = erDb.getEntities(); + expect(entities.hasOwnProperty('DUCK-BILLED-PLATYPUS')).toBe(true); + expect(entities.hasOwnProperty('CHARACTER_SET')).toBe(true); + }); }); it('should allow an entity with a single attribute to be defined', function () { @@ -447,27 +545,23 @@ describe('when parsing ER diagram it...', function () { }).toThrowError(); }); - it('should allow an empty quoted label', function () { - erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : ""'); - const rels = erDb.getRelationships(); - expect(rels[0].roleA).toBe(''); - }); - - it('should allow an non-empty quoted label', function () { - erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : "places"'); - const rels = erDb.getRelationships(); - expect(rels[0].roleA).toBe('places'); - }); - - it('should allow an non-empty unquoted label', function () { - erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : places'); - const rels = erDb.getRelationships(); - expect(rels[0].roleA).toBe('places'); - }); - - it('should allow an entity name with a dot', function () { - erDiagram.parser.parse('erDiagram\nCUSTOMER.PROP ||--|{ ORDER : places'); - const rels = erDb.getRelationships(); - expect(rels[0].entityA).toBe('CUSTOMER.PROP'); + describe('relationship labels', function () { + it('should allow an empty quoted label', function () { + erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : ""'); + const rels = erDb.getRelationships(); + expect(rels[0].roleA).toBe(''); + }); + + it('should allow an non-empty quoted label', function () { + erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : "places"'); + const rels = erDb.getRelationships(); + expect(rels[0].roleA).toBe('places'); + }); + + it('should allow an non-empty unquoted label', function () { + erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : places'); + const rels = erDb.getRelationships(); + expect(rels[0].roleA).toBe('places'); + }); }); });