From 6fd26ce50f5cbe6a96322a6fc9db6926e801be50 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Tue, 4 Jun 2024 15:52:19 +0200 Subject: [PATCH 1/2] feat: allow to provide dialect This enables me as a user to specify the FEEL dialect to use, one of `expression` or `unaryTests`. --- README.md | 11 +++++++- src/index.js | 6 ++++- src/language/index.js | 12 ++++++--- test/spec/CodeEditor.spec.js | 50 ++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ef881e3..51f5729 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,16 @@ const editor = new FeelEditor({ }); ``` -Optionally, you can provide a starting document and listen for changes: +Configure the FEEL dialect (expression or unary tests): + +```JavaScript +const editor = new FeelEditor({ + container, + dialect: 'unaryTests' // defaults to 'expression' +}); +``` + +You can provide a starting document and listen for changes: ```JavaScript const editor = new FeelEditor({ diff --git a/src/index.js b/src/index.js index 585bea8..304ebcf 100644 --- a/src/index.js +++ b/src/index.js @@ -32,6 +32,7 @@ const placeholderConf = new Compartment(); * @param {Object} config * @param {DOMNode} config.container * @param {Extension[]} [config.extensions] + * @param {'expression' | 'unaryTests'} [config.dialect='expression'] * @param {DOMNode|String} [config.tooltipContainer] * @param {Function} [config.onChange] * @param {Function} [config.onKeyDown] @@ -44,6 +45,7 @@ const placeholderConf = new Compartment(); */ export default function FeelEditor({ extensions: editorExtensions = [], + dialect = 'expression', container, contentAttributes = {}, tooltipContainer, @@ -104,7 +106,9 @@ export default function FeelEditor({ keymap.of([ ...defaultKeymap, ]), - language(), + language({ + dialect + }), linter, lintHandler, placeholderConf.of(placeholderExt(placeholder)), diff --git a/src/language/index.js b/src/language/index.js index cefa73d..5019317 100644 --- a/src/language/index.js +++ b/src/language/index.js @@ -1,6 +1,10 @@ -import { LanguageSupport } from '@codemirror/language'; -import { feelLanguage } from 'lang-feel'; +import { feel } from 'lang-feel'; -export function language() { - return new LanguageSupport(feelLanguage, [ ]); +/** + * @param {'expression' | 'unaryTests'} dialect + * + * @return {LanguageSupport} + */ +export function language(dialect) { + return feel(dialect); } diff --git a/test/spec/CodeEditor.spec.js b/test/spec/CodeEditor.spec.js index fdfe855..32dc29f 100644 --- a/test/spec/CodeEditor.spec.js +++ b/test/spec/CodeEditor.spec.js @@ -133,6 +133,22 @@ return }); + it('should configure dialect', async function() { + + // when + const initialValue = '"Hello", "World"'; + const editor = new FeelEditor({ + container, + value: initialValue, + dialect: 'unaryTests' + }); + + // then + expect(editor).to.exist; + expect(editor._cmEditor.state.doc.toString()).to.equal('"Hello", "World"'); + }); + + it('should allow for extensions', async function() { // when @@ -520,6 +536,40 @@ return }); + describe('should not highlight valid', function() { + + [ + { dialect: 'expression', value: 'Mike < 10' }, + { dialect: 'unaryTests', value: '12, now(), "STRING"' } + ].forEach(({ dialect, value }) => { + + it(`<${dialect}>`, function(done) { + + // given + const editor = new FeelEditor({ + container, + value, + dialect + }); + + const cm = getCm(editor); + + // when + forceLinting(cm); + + // then + // update done async + setTimeout(() => { + expect(diagnosticCount(cm.state)).to.eql(0); + done(); + }, 0); + }); + + }); + + }); + + it('should highlight unexpected operations', function(done) { const initalValue = '= 15'; From c17e4a411fa6aa672d09729ccb93ca7134ea3256 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Tue, 4 Jun 2024 15:53:39 +0200 Subject: [PATCH 2/2] test: improve editor linting spec * get rid of internals (provide test utility) * simplifies writing tests (and understanding them) --- test/spec/CodeEditor.spec.js | 140 ++++++++++++++++------------------- 1 file changed, 63 insertions(+), 77 deletions(-) diff --git a/test/spec/CodeEditor.spec.js b/test/spec/CodeEditor.spec.js index 32dc29f..564469d 100644 --- a/test/spec/CodeEditor.spec.js +++ b/test/spec/CodeEditor.spec.js @@ -513,7 +513,7 @@ return describe('lint', function() { - it('should not highlight empty document', function(done) { + it('should not highlight empty document', async function() { const initalValue = ''; const editor = new FeelEditor({ @@ -521,18 +521,11 @@ return value: initalValue }); - const cm = getCm(editor); - // when - forceLinting(cm); + const diagnostics = await lint(editor); // then - // update done async - setTimeout(() => { - expect(diagnosticCount(cm.state)).to.eql(0); - done(); - }, 0); - + expect(diagnostics).to.eql(0); }); @@ -543,7 +536,7 @@ return { dialect: 'unaryTests', value: '12, now(), "STRING"' } ].forEach(({ dialect, value }) => { - it(`<${dialect}>`, function(done) { + it(`<${dialect}>`, async function() { // given const editor = new FeelEditor({ @@ -552,17 +545,11 @@ return dialect }); - const cm = getCm(editor); - // when - forceLinting(cm); + const diagnostics = await lint(editor); // then - // update done async - setTimeout(() => { - expect(diagnosticCount(cm.state)).to.eql(0); - done(); - }, 0); + expect(diagnostics).to.eql(0); }); }); @@ -570,7 +557,7 @@ return }); - it('should highlight unexpected operations', function(done) { + it('should highlight unexpected operations', async function() { const initalValue = '= 15'; const editor = new FeelEditor({ @@ -578,22 +565,15 @@ return value: initalValue }); - const cm = getCm(editor); - // when - forceLinting(cm); + const diagnostics = await lint(editor); // then - // update done async - setTimeout(() => { - expect(diagnosticCount(cm.state)).to.eql(1); - done(); - }, 0); - + expect(diagnostics).to.eql(1); }); - it('should highlight missing operations', function(done) { + it('should highlight missing operations', async function() { const initalValue = '15 == 15'; const editor = new FeelEditor({ @@ -601,75 +581,56 @@ return value: initalValue }); - const cm = getCm(editor); - // when - forceLinting(cm); + const diagnostics = await lint(editor); // then - // update done async - setTimeout(() => { - expect(diagnosticCount(cm.state)).to.eql(1); - done(); - }, 0); - + expect(diagnostics).to.eql(1); }); - it('should call onLint with errors', function(done) { - const initalValue = '= 15'; - const onLint = sinon.spy(); + describe('should call onLint', function() { - const editor = new FeelEditor({ - container, - value: initalValue, - onLint - }); + it('with errors', async function() { - const cm = getCm(editor); + const initalValue = '= 15'; + const onLint = sinon.spy(); - // when - forceLinting(cm); + const editor = new FeelEditor({ + container, + value: initalValue, + onLint + }); - // then - // update done async - setTimeout(() => { + // when + await lint(editor); + // then expect(onLint).to.have.been.calledOnce; expect(onLint).to.have.been.calledWith(sinon.match.array); expect(onLint.args[0][0]).to.have.length(1); - - done(); - }, 0); - - }); - - - it('should call onLint without errors', function(done) { - const initalValue = '15'; - const onLint = sinon.spy(); - - const editor = new FeelEditor({ - container, - value: initalValue, - onLint }); - const cm = getCm(editor); - // when - forceLinting(cm); + it('without errors', async function() { + const initalValue = '15'; + const onLint = sinon.spy(); - // then - // update done async - setTimeout(() => { + const editor = new FeelEditor({ + container, + value: initalValue, + onLint + }); + + // when + await lint(editor); + // then + // update done async expect(onLint).to.have.been.calledOnce; expect(onLint).to.have.been.calledWith(sinon.match.array); expect(onLint.args[0][0]).to.have.length(0); - - done(); - }, 0); + }); }); @@ -934,6 +895,11 @@ function select(editor, anchor, head = anchor) { }); } +/** + * @param {FeelEditor} editor + * + * @return {import('@codemirror/view').EditorView} + */ function getCm(editor) { return editor._cmEditor || editor; } @@ -956,3 +922,23 @@ async function expectEventually(fn) { throw e; } + +function wait(ms = 0) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +/** + * @param {FeelEditor} editor + * + * @return {Promise} + */ +async function lint(editor) { + const cm = getCm(editor); + + // when + forceLinting(cm); + + await wait(); + + return diagnosticCount(cm.state); +} \ No newline at end of file