diff --git a/src/mixins/extensions/LoopContainer.js b/src/mixins/extensions/LoopContainer.js index 3914a7cca..8eca236a1 100644 --- a/src/mixins/extensions/LoopContainer.js +++ b/src/mixins/extensions/LoopContainer.js @@ -25,6 +25,7 @@ export default { items: element.items, }, ], + watchers: definition.watchers, }; let loopContext = ''; diff --git a/src/mixins/extensions/Watchers.js b/src/mixins/extensions/Watchers.js index d374bcb70..a4a79da07 100644 --- a/src/mixins/extensions/Watchers.js +++ b/src/mixins/extensions/Watchers.js @@ -2,17 +2,24 @@ import watchersMixin from '../../mixins/watchers'; export default { methods: { + filterWatchers(watcher) { + const inContext = !watcher.watching || this.variables.find(({name}) => name === watcher.watching); + return inContext; + }, addWatcherVariables(definition) { if (definition.watchers) { - definition.watchers.forEach((watcher) => { - this.registerVariable(watcher.output_variable, {}); + definition.watchers.filter(this.filterWatchers).forEach((watcher) => { + const inContext = !watcher.watching || this.variables.find(({name}) => name === watcher.watching); + if (inContext) { + this.registerVariable(watcher.output_variable, {}); + } }); } }, watchers(screen, definition) { if (definition.watchers) { screen.mixins.push(watchersMixin); - definition.watchers.forEach((watcher) => { + definition.watchers.filter(this.filterWatchers).forEach((watcher) => { this.addMounted(screen, ` this.$nextTick(() => this.$watch('${watcher.watching}', (newValue) => { if (typeof newValue !== 'undefined') { diff --git a/tests/e2e/fixtures/watcher_inside_loop.json b/tests/e2e/fixtures/watcher_inside_loop.json new file mode 100644 index 000000000..437ed7379 --- /dev/null +++ b/tests/e2e/fixtures/watcher_inside_loop.json @@ -0,0 +1 @@ +{"type":"screen_package","version":"2","screens":[{"id":5,"screen_category_id":"1","title":"Watcher in Loop","description":"Watcher in Loop","type":"FORM","config":[{"name":"Watcher in Loop","items":[{"items":[{"label":"Line Input","config":{"icon":"far fa-square","name":"form_input_1","type":"text","label":"New Input","helper":null,"readonly":false,"dataFormat":"string","validation":[],"placeholder":null},"component":"FormInput","inspector":[{"type":"FormInput","field":"name","config":{"name":"Variable Name","label":"Variable Name","helper":"A variable name is a symbolic name to reference information.","validation":"regex:\/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*[^.]$\/|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false"}},{"type":"FormInput","field":"label","config":{"label":"Label","helper":"The label describes the field's name"}},{"type":"FormMultiselect","field":"dataFormat","config":{"name":"Data Type","label":"Data Type","helper":"The data type specifies what kind of data is stored in the variable.","options":[{"value":"string","content":"Text"},{"value":"int","content":"Integer"},{"value":"currency","content":"Currency"},{"value":"percentage","content":"Percentage"},{"value":"float","content":"Decimal"},{"value":"datetime","content":"Datetime"},{"value":"date","content":"Date"},{"value":"password","content":"Password"}],"validation":"required"}},{"type":{"extends":{"props":["label","error","options","helper","name","value","selectedControl"],"mixins":[{"props":{"validation":{"type":null},"validationData":{"type":null},"validationField":{"type":null},"validationMessages":{"type":null}},"watch":{"validationData":{"deep":true}},"methods":[],"computed":[]}],"methods":[],"computed":[],"_compiled":true,"inheritAttrs":false,"staticRenderFns":[]},"computed":[],"_compiled":true,"staticRenderFns":[]},"field":"dataMask","config":{"name":"Data Format","label":"Data Format","helper":"The data format for the selected type."}},{"type":"ValidationSelect","field":"validation","config":{"label":"Validation Rules","helper":"The validation rules needed for this field"}},{"type":"FormInput","field":"placeholder","config":{"label":"Placeholder Text","helper":"The placeholder is what is shown in the field when no value is provided yet"}},{"type":"FormInput","field":"helper","config":{"label":"Helper Text","helper":"Help text is meant to provide additional guidance on the field's value"}},{"type":"FormCheckbox","field":"readonly","config":{"label":"Read Only","helper":null}},{"type":"ColorSelect","field":"color","config":{"label":"Text Color","helper":"Set the element's text color","options":[{"value":"text-primary","content":"primary"},{"value":"text-secondary","content":"secondary"},{"value":"text-success","content":"success"},{"value":"text-danger","content":"danger"},{"value":"text-warning","content":"warning"},{"value":"text-info","content":"info"},{"value":"text-light","content":"light"},{"value":"text-dark","content":"dark"}]}},{"type":"ColorSelect","field":"bgcolor","config":{"label":"Background Color","helper":"Set the element's background color","options":[{"value":"alert alert-primary","content":"primary"},{"value":"alert alert-secondary","content":"secondary"},{"value":"alert alert-success","content":"success"},{"value":"alert alert-danger","content":"danger"},{"value":"alert alert-warning","content":"warning"},{"value":"alert alert-info","content":"info"},{"value":"alert alert-light","content":"light"},{"value":"alert alert-dark","content":"dark"}]}},{"type":{"props":["value","helper"],"watch":{"value":{"immediate":true}},"methods":[],"_scopeId":"data-v-172d71e1","computed":{"effectiveValue":[]},"_compiled":true,"components":{"MonacoEditor":{"name":"monaco-editor","_Ctor":[],"props":{"amdRequire":[]},"extends":{"name":"MonacoEditor","model":{"event":"change"},"props":{"theme":{"default":"vs"},"value":{"required":true},"options":[],"language":[],"original":[],"amdRequire":[],"diffEditor":{"default":false}},"watch":{"options":{"deep":true,"user":true}},"methods":[]},"methods":[]}},"staticRenderFns":[]},"field":"defaultValue","config":{"label":"Default Value","helper":"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed."}},{"type":"FormInput","field":"conditionalHide","config":{"label":"Visibility Rule","helper":"This control is hidden until this expression is true"}},{"type":"FormInput","field":"customFormatter","config":{"label":"Custom Format String","helper":"Use the Mask Pattern format
Date ##\/##\/####
SSN ###-##-####
Phone (###) ###-####","validation":null}},{"type":"FormInput","field":"customCssSelector","config":{"label":"CSS Selector Name","helper":"Use this in your custom css rules","validation":"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]"}},{"type":"FormInput","field":"ariaLabel","config":{"label":"Aria Label","helper":"Attribute designed to help assistive technology (e.g. screen readers) attach a label"}},{"type":"FormInput","field":"tabindex","config":{"label":"Tab Order","helper":"Order in which a user will move focus from one control to another by pressing the Tab key","validation":"regex: [0-9]*"}}],"editor-control":"FormInput","editor-component":"FormInput"}],"label":"Loop","config":{"icon":"fas fa-redo","name":"loop_1","label":null,"settings":{"add":false,"type":"new","times":"3","varname":"loop_1"}},"component":"FormLoop","container":true,"inspector":[{"type":"LoopInspector","field":"settings","config":[]},{"type":"FormInput","field":"conditionalHide","config":{"label":"Visibility Rule","helper":"This control is hidden until this expression is true"}},{"type":"FormInput","field":"customFormatter","config":{"label":"Custom Format String","helper":"Use the Mask Pattern format
Date ##\/##\/####
SSN ###-##-####
Phone (###) ###-####","validation":null}},{"type":"FormInput","field":"customCssSelector","config":{"label":"CSS Selector Name","helper":"Use this in your custom css rules","validation":"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]"}},{"type":"FormInput","field":"ariaLabel","config":{"label":"Aria Label","helper":"Attribute designed to help assistive technology (e.g. screen readers) attach a label"}},{"type":"FormInput","field":"tabindex","config":{"label":"Tab Order","helper":"Order in which a user will move focus from one control to another by pressing the Tab key","validation":"regex: [0-9]*"}}],"editor-control":"Loop","editor-component":"Loop"}]}],"computed":[],"custom_css":null,"created_at":"2021-11-05T15:35:12-07:00","updated_at":"2021-11-05T15:37:47-07:00","status":"ACTIVE","key":null,"watchers":[{"input_data":"{}","script_configuration":"{}","synchronous":false,"show_async_loading":false,"run_onload":false,"name":"watcher inside loop","watching":"form_input_1","output_variable":"inside_loop","script":{"id":"script-6","key":null,"title":"simple","description":"simple","language":"php","code":"users()->getUserById(1)['email'] \n * API Documentation https:\/\/github.com\/ProcessMaker\/docker-executor-php\/tree\/master\/docs\/sdk \n *\/\n\nreturn [\"foo\"=>\"bar\"];","timeout":60,"run_as_user_id":2,"created_at":"2021-11-05T15:36:35-07:00","updated_at":"2021-11-05T15:36:52-07:00","status":"ACTIVE","script_category_id":"1","script_executor_id":3},"script_id":"6","script_key":null,"uid":"16361517695581"}],"categories":[{"id":1,"name":"Uncategorized","status":"ACTIVE","is_system":0,"created_at":"2021-11-01T11:16:52-07:00","updated_at":"2021-11-01T11:16:52-07:00","pivot":{"assignable_id":5,"category_id":1,"category_type":"ProcessMaker\\Models\\ScreenCategory"}}]}],"screen_categories":[],"scripts":[{"id":6,"key":null,"title":"simple","description":"simple","language":"php","code":"users()->getUserById(1)['email'] \n * API Documentation https:\/\/github.com\/ProcessMaker\/docker-executor-php\/tree\/master\/docs\/sdk \n *\/\n\nreturn [\"foo\"=>\"bar\"];","timeout":60,"run_as_user_id":2,"created_at":"2021-11-05T15:36:35-07:00","updated_at":"2021-11-05T15:36:52-07:00","status":"ACTIVE","script_category_id":"1","script_executor_id":3,"categories":[{"id":1,"name":"Uncategorized","status":"ACTIVE","is_system":0,"created_at":"2021-11-01T11:16:52-07:00","updated_at":"2021-11-01T11:16:52-07:00","pivot":{"assignable_id":6,"category_id":1,"category_type":"ProcessMaker\\Models\\ScriptCategory"}}]}]} \ No newline at end of file diff --git a/tests/e2e/specs/LoadComplexData.spec.js b/tests/e2e/specs/LoadComplexData.spec.js index e57be9199..b65b1cd9e 100644 --- a/tests/e2e/specs/LoadComplexData.spec.js +++ b/tests/e2e/specs/LoadComplexData.spec.js @@ -8,7 +8,7 @@ describe('Validation Rules (Advanced test)', () => { cy.loadFromJson('FPP_PFP_CHAIRS_SCREEN.json', 0); cy.get('[data-cy=mode-preview]').click(); // Open Tab 2 - cy.get('[aria-label="Part Time"]:visible:first').click(); + cy.get('button:contains("Part Time"):visible:first').click(); // Tab 2 contains the title '2. PART TIME PAGE' cy.get('#preview').should('contain.html', '2. PART TIME PAGE'); diff --git a/tests/e2e/specs/WatcherInsideLoop.spec.js b/tests/e2e/specs/WatcherInsideLoop.spec.js new file mode 100644 index 000000000..b9a7f8676 --- /dev/null +++ b/tests/e2e/specs/WatcherInsideLoop.spec.js @@ -0,0 +1,114 @@ +describe('watcher inside loop', () => { + beforeEach(() => { + cy.server(); + cy.visit('/'); + cy.route( + 'POST', + '/api/1.0/scripts/execute/6', + JSON.stringify({ + output: { + foo: 'bar', + }, + }) + ).as('executeScript'); + }); + + it('Verify watcher is placed inside loop', () => { + cy.loadFromJson('watcher_inside_loop.json', 0); + cy.get('[data-cy=mode-preview]').click(); + + // Check the data of the screen + cy.assertPreviewData({ + 'loop_1': [ + { + 'form_input_1': '', + 'inside_loop': null, + }, + { + 'form_input_1': '', + 'inside_loop': null, + }, + { + 'form_input_1': '', + 'inside_loop': null, + }, + ], + }); + + // Change form_input_1 to trigger watcher in the first loop + cy.get('[data-cy="screen-field-form_input_1"]').eq(0).type('foo'); + cy.wait('@executeScript'); + // Check the data of the screen + cy.assertPreviewData({ + 'loop_1': [ + { + 'form_input_1': 'foo', + 'inside_loop': { + foo: 'bar', + }, + }, + { + 'form_input_1': '', + 'inside_loop': null, + }, + { + 'form_input_1': '', + 'inside_loop': null, + }, + ], + }); + + // Change form_input_1 to trigger watcher in the second loop + cy.get('[data-cy="screen-field-form_input_1"]').eq(1).type('foo'); + cy.wait('@executeScript'); + // Check the data of the screen + cy.assertPreviewData({ + 'loop_1': [ + { + 'form_input_1': 'foo', + 'inside_loop': { + foo: 'bar', + }, + }, + { + 'form_input_1': 'foo', + 'inside_loop': { + foo: 'bar', + }, + }, + { + 'form_input_1': '', + 'inside_loop': null, + }, + ], + }); + + // Change form_input_1 to trigger watcher in the third loop + cy.get('[data-cy="screen-field-form_input_1"]').eq(2).type('foo'); + cy.wait('@executeScript'); + // Check the data of the screen + cy.assertPreviewData({ + 'loop_1': [ + { + 'form_input_1': 'foo', + 'inside_loop': { + foo: 'bar', + }, + }, + { + 'form_input_1': 'foo', + 'inside_loop': { + foo: 'bar', + }, + }, + { + 'form_input_1': 'foo', + 'inside_loop': { + foo: 'bar', + }, + }, + ], + }); + + }); +});