diff --git a/index.html b/index.html index fd1b6605..65f06339 100644 --- a/index.html +++ b/index.html @@ -36,7 +36,6 @@ CKEDITOR_BASEPATH = 'ckeditor/'; window.less = {async: true, fileSync: true}; - \ No newline at end of file diff --git a/src/variables/editor/editor.js b/src/variables/editor/editor.js index 85de8256..261e310b 100644 --- a/src/variables/editor/editor.js +++ b/src/variables/editor/editor.js @@ -2,6 +2,7 @@ import DefineMap from 'can-define/map/map' import Component from 'can-component' import template from './editor.stache' import constants from '~/src/models/constants' +import { vcGatherUsage } from './vcGatherUsage' export const VariableEditorVM = DefineMap.extend('VariableEditorVM', { /* @@ -150,7 +151,7 @@ export const VariableEditorVM = DefineMap.extend('VariableEditorVM', { onFindUsage () { // use the initially loaded name in case they've edited it in the form before checking usage const variableName = this.initialVarName - const html = window.vcGatherUsage(variableName) + const html = vcGatherUsage(variableName) this.variableUsageHtml = html } }) diff --git a/src/variables/editor/vcGatherUsage-test.html b/src/variables/editor/vcGatherUsage-test.html new file mode 100644 index 00000000..07a2a262 --- /dev/null +++ b/src/variables/editor/vcGatherUsage-test.html @@ -0,0 +1,15 @@ + + + +vcGatherUsage Tests + +
+
+ + + + diff --git a/src/variables/editor/vcGatherUsage-test.js b/src/variables/editor/vcGatherUsage-test.js new file mode 100644 index 00000000..b6c4aab3 --- /dev/null +++ b/src/variables/editor/vcGatherUsage-test.js @@ -0,0 +1,166 @@ +import 'jquery' +import { assert } from 'chai' +import 'steal-mocha' +import { vcGatherUsage, findMacroMatches, findLogicMatches, findLiteralMatches, findMatches} from './vcGatherUsage' + +// Tests for these functions reflect that varname and testValue will be lowercase by the point they reach these functions + +describe('findMacroMatches', function () { + it.only('returns matches based on A2J Macro Syntax', function () { + const matches = findMacroMatches('Enter your mailing address, %%[Client First Name TE ]%%', 'Client First Name TE') + const matches2 = findMacroMatches('Enter your mailing address, %%[Client First Name TE D]%%', 'Client First Name TE') + const matches3 = findMacroMatches('Enter your mailing address, %%[ Client First Name TE ]%%', 'Client First Name TE') + + //explicit = true should pass, fail, pass + //explicit = false should pass all + + assert.deepEqual(matches, ['[Client First Name TE ]'], 'Should find one match') + assert.deepEqual(matches2, ['[Client First Name TE D]'], 'Should find one match') + assert.deepEqual(matches3, [ '[ Client First Name TE ]' ], 'Should find one match') + }) +}) + +describe('findLogicMatches', function () { + it.only('Returns true search target is matched', function () { + const matches = findLogicMatches('set client first name te to "matt"','client first name te') + const matches2 = findLogicMatches("set [client last name te] to 'matt'",'client first name te') + + assert.deepEqual(matches, true, "should return true if match is found") + assert.deepEqual(matches2, null, "should return null if no match is found") + }) +}) + +describe('findLiteralMatches', function () { + it.only('Returns true if target matches exactly', function () { + const matches = findLiteralMatches('client age', 'clientage') + const matches2 = findLiteralMatches('client age', 'CLIENT AGE') + + assert.deepEqual(matches, [], "should return null if no match found") + assert.deepEqual(matches2, [ 'client age'], "Returns true if exact match") + }) +}) + +//Find Matches currently needs regex type things to have bracket, paren, percent, logic and literal +describe('findMatches', function () { + it.only('Returns found if it finds a match', function() { + const testButton = {name:'', label:'', value:'', repeatVar:' [CLIENT FIRST NAME TE] ', url:''} + const testEntry = {key: 'repeatVar', type: 'regex', display: 'Button Counting Variable' } + const testVarName = 'Client First Name TE' + + const found = findMatches(testButton, testEntry, testVarName) + + assert.deepEqual(found, [ '[client first name te]' ], "testing") + }) +}) + +describe('vcGatherUsage', function () { + let usageTestPage + const testProps = { + page: [ + { key: 'name', type: 'regex', display: 'Page Name' }, + { key: 'text', type: 'regex', display: 'Question Text' }, + { key: 'repeatVar', type: 'string', display: 'Counting Variable' }, + { key: 'outerLoopVar', type: 'string', display: 'Outer Loop Variable' }, + { key: 'learn', type: 'regex', display: 'LearnMore Prompt' }, + { key: 'help', type: 'regex', display: 'LearnMore Response' }, + { key: 'helpReader', type: 'regex', display: 'Video Transcript' }, + { key: 'codeBefore', type: 'logic', display: 'Before Logic' }, + { key: 'codeAfter', type: 'logic', display: 'After Logic' } + ], + fields: [ + { key: 'label', type: 'regex', display: 'Field Label' }, + { key: 'name', type: 'string', display: 'Field Variable' }, + { key: 'value', type: 'regex', display: 'Field Default Value' }, + { key: 'invalidPrompt', type: 'regex', display: 'Field Custom Invalid Prompt' }, + { key: 'sample', type: 'regex', display: 'Field Sample Value' } + ], + buttons: [ + { key: 'label', type: 'regex', display: 'Button Label' }, + { key: 'name', type: 'string', display: 'Button Variable Name' }, + { key: 'value', type: 'regex', display: 'Button Default Value' }, + { key: 'repeatVar', type: 'string', display: 'Button Counting Variable' }, + { key: 'url', type: 'regex', display: 'Button URL' } + ] + } + + beforeEach(() => { + usageTestPage = new window.TPage() + usageTestPage.fields = [new window.TField()] + usageTestPage.buttons = [new window.TButton()] + + const gGuide = new window.TGuide() + window.gGuide = gGuide + window.gGuide.pages = { usageTestPage } + }) + + afterEach(() => { + window.gGuide = null + }) + + it('gathers usage on Page, Field, and Button level property values using variables', function () { + const setTestProps = (targetMap, propsToSet) => { + for (const entry of propsToSet) { + const prop = entry.key + if (entry.type === 'regex') { + targetMap[prop] = 'macro style %%[Number NU]%%' + } else if (entry.type === 'logic') { + targetMap[prop] = 'SET [Number NU] TO 1' + } else { // direct var set + targetMap[prop] = 'Number NU' + } + } + } + setTestProps(usageTestPage, testProps.page) + setTestProps(usageTestPage.fields[0], testProps.fields) + setTestProps(usageTestPage.buttons[0], testProps.buttons) + + const foundMessage = vcGatherUsage('Number NU') + // if found, `display value` will be in foundMessage for each entry + for (const entry of testProps.page) { + const usedInPage = foundMessage.indexOf(entry.display) !== -1 + assert.isTrue(usedInPage, `should find Number NU usage in page.${entry.key} by displaying ${entry.display} in returned foundMessage html`) + } + for (const entry of testProps.fields) { + const usedInField = foundMessage.indexOf(entry.display) !== -1 + assert.isTrue(usedInField, `should find Number NU usage in field.${entry.key} by displaying ${entry.display} in returned foundMessage html`) + } + for (const entry of testProps.buttons) { + const usedInButton = foundMessage.indexOf(entry.display) !== -1 + assert.isTrue(usedInButton, `should find Number NU usage in button.${entry.key} by displaying ${entry.display} in returned foundMessage html`) + } + }) + + it('gathers explicit usage on Page, Field, and Button level property values using variables', function () { + const setTestProps = (targetMap, propsToSet) => { + for (const entry of propsToSet) { + const prop = entry.key + if (entry.type === 'regex') { + targetMap[prop] = 'macro style %%[Number NU]%%' + } else if (entry.type === 'logic') { + targetMap[prop] = 'SET [Number123 NU] TO 1' + } else { // direct var set + targetMap[prop] = 'Number foo NU' + } + } + } + + setTestProps(usageTestPage, testProps.page) + setTestProps(usageTestPage.fields[0], testProps.fields) + setTestProps(usageTestPage.buttons[0], testProps.buttons) + + const foundMessage = vcGatherUsage('Number NU') + // if found, `display value` will be in foundMessage for each entry + for (const entry of testProps.page) { + const usedInPage = foundMessage.indexOf(entry.display) !== -1 + assert.isTrue(usedInPage, `should find Number NU usage in page.${entry.key} by displaying ${entry.display} in returned foundMessage html`) + } + for (const entry of testProps.fields) { + const usedInField = foundMessage.indexOf(entry.display) !== -1 + assert.isTrue(usedInField, `should find Number NU usage in field.${entry.key} by displaying ${entry.display} in returned foundMessage html`) + } + for (const entry of testProps.buttons) { + const usedInButton = foundMessage.indexOf(entry.display) !== -1 + assert.isTrue(usedInButton, `should find Number NU usage in button.${entry.key} by displaying ${entry.display} in returned foundMessage html`) + } + }) +}) diff --git a/src/variables/editor/vcGatherUsage.js b/src/variables/editor/vcGatherUsage.js new file mode 100644 index 00000000..52f7c47f --- /dev/null +++ b/src/variables/editor/vcGatherUsage.js @@ -0,0 +1,152 @@ +import { lowerCase } from "lodash" + +const pageProps = [ + { key: 'name', type: 'regex', display: 'Page Name' }, + { key: 'text', type: 'regex', display: 'Question Text' }, + { key: 'repeatVar', type: 'string', display: 'Counting Variable' }, + { key: 'outerLoopVar', type: 'string', display: 'Outer Loop Variable' }, + { key: 'learn', type: 'regex', display: 'LearnMore Prompt' }, + { key: 'help', type: 'regex', display: 'LearnMore Response' }, + { key: 'helpReader', type: 'regex', display: 'Video Transcript' }, + { key: 'codeBefore', type: 'logic', display: 'Before Logic' }, + { key: 'codeAfter', type: 'logic', display: 'After Logic' } +] +const fieldProps = [ + { key: 'label', type: 'regex', display: 'Field Label' }, + { key: 'name', type: 'string', display: 'Field Variable' }, + { key: 'value', type: 'regex', display: 'Field Default Value' }, + { key: 'invalidPrompt', type: 'regex', display: 'Field Custom Invalid Prompt' }, + { key: 'sample', type: 'regex', display: 'Field Sample Value' } +] +const buttonProps = [ + { key: 'label', type: 'regex', display: 'Button Label' }, + { key: 'name', type: 'string', display: 'Button Variable Name' }, + { key: 'value', type: 'regex', display: 'Button Default Value' }, + { key: 'repeatVar', type: 'string', display: 'Button Counting Variable' }, + { key: 'url', type: 'regex', display: 'Button URL' } +] + +/* + +Type Match = { +pageName: +location: +foundCount: +} + +*/ + +export const findMacroMatches = (testValue, lowerCaseVarName, explicit) => { + explicit = true; + + //For greedy search, Regex looks for lowerCaseVarName followed by any character 0+ times, then white space 0+ times, then the closing half of bracket, paren, percent + const parenRegexString = `\\(\\s*${lowerCaseVarName}.*\\s*\\)` + const percentRegexString = `\\%\\s*${lowerCaseVarName}.*\\s*\\%` + const bracketRegexString = `\\[\\s*${lowerCaseVarName}.*\\s*\\]` + + //For explicit search, Regex ends with '$', meaning nothing can follow + const parenRegexStringX = `\\(\\s*${lowerCaseVarName}\\s*\\)` + const percentRegexStringX = `\\%\\s*${lowerCaseVarName}\\s*\\%` + const bracketRegexStringX = `\\[\\s*${lowerCaseVarName}\\s*\\]` + + const regexString = `${parenRegexString}|${percentRegexString}|${bracketRegexString}` + const regexStringX = `${parenRegexStringX}|${percentRegexStringX}|${bracketRegexStringX}` + + //Checks explicit and uses the appropriate regexString to make macroRegex + const macroRegex = (explicit === false) ? new RegExp(regexString, 'ig'): new RegExp(regexStringX, 'ig') + + const matches = testValue.match(macroRegex) + + return matches ? matches :[] +} + +export const findLogicMatches = (testValue, lowerCaseVarName) => { + const logicRegexString = `${lowerCaseVarName}` + const logicRegex = new RegExp(logicRegexString, 'ig') + + const matches = testValue.match(logicRegex) + + return matches ? matches :[] +} + +export const findLiteralMatches =(testValue, lowerCaseVarName) => { + // finds variables assigned explicitly to buttons, fields, and counting variables + const literalRegexString = `${lowerCaseVarName}` + const literalRegex = new RegExp(literalRegexString, 'ig') + + const matches = testValue.match(literalRegex) + + return matches ? matches :[] +} + +export const findMatches = (searchTarget, usageItem, varName) => { + // skip check if not string value to check + const prop = usageItem.key + if (!searchTarget[prop]) { return } + + let found = [] + let explicit + + const testValue = searchTarget[prop].toLowerCase() + const lowerCaseVarName = varName.toLowerCase() + + if (usageItem.type === 'regex') { // check for macro matches, `%%someVar%%` + found = findMacroMatches(testValue, lowerCaseVarName, explicit) + } else if (usageItem.type === 'logic') { + found = findLogicMatches(testValue, lowerCaseVarName) + } else { + found = findLiteralMatches(testValue, lowerCaseVarName) + } + + return (found.length !== 0) ? usageItem.display : [] +} + +export function vcGatherUsage (varName, explicitSearch) { // 2015-03-27 Search for variable or constant + // alter below line to test two search methods + explicitSearch = true + let html = '' + let count = 0 + let pageName + + for (pageName in window.gGuide.pages) { // Search text, buttons, help, fields and logic for variable name. + /** @type TPage */ + let where = [] // list where it's on this page + let pageMatches, fieldMatches, buttonMatches + + const page = window.gGuide.pages[pageName] + + // check top level page properties + for (const entry of pageProps) { + pageMatches = findMatches(page, entry, varName) + if(pageMatches && pageMatches.length > 1) { + where = [...where, pageMatches] + } + } + + // check all page fields + for (const field of page.fields) { + for (const entry of fieldProps) { + fieldMatches = findMatches(field, entry, varName) + if(fieldMatches && fieldMatches.length > 1) { + where = [...where, fieldMatches] + } + } + } + + // check all buttons + for (const button of page.buttons) { + for (const entry of buttonProps) { + buttonMatches = findMatches(button, entry, varName) + if(buttonMatches && buttonMatches.length > 1) { + where = [...where, buttonMatches] + } + } + } + + if (where.length) { // If we found anything, we'll list the page and its location. + count++ + html += ('
  • ' + page.name + '
  • ') + } + } + return 'Used in ' + count + ' pages' + '' +} diff --git a/test/test.js b/test/test.js index 6195ceaf..32f913a4 100644 --- a/test/test.js +++ b/test/test.js @@ -23,3 +23,4 @@ import 'a2jauthor/src/pages-tab/components/var-picker/field/var-picker-field-tes // import 'a2jauthor/src/templates/edit/toolbar/toolbar-test' // import 'a2jauthor/src/templates/list/item/item-test' // import 'a2jauthor/src/templates/list/sortbar/sortbar-test' +import 'a2jauthor/src/variables/editor/vcGatherUsage-test.js'