From 3f5a685cdaf81b54f798909170ef57e8707001e9 Mon Sep 17 00:00:00 2001 From: mitch Date: Mon, 2 Dec 2024 13:45:43 -0500 Subject: [PATCH] findVarUsage add tests and cleanup --- src/variables/editor/editor-test.js | 19 ++- src/variables/editor/editor.js | 19 ++- src/variables/editor/findVarUsage.js | 95 ------------ src/variables/editor/util/findVarUsage.js | 72 +++++++++ src/variables/editor/util/propsLists.js | 27 ++++ src/variables/editor/{ => util}/testPages.js | 152 +++++++++++-------- 6 files changed, 208 insertions(+), 176 deletions(-) delete mode 100644 src/variables/editor/findVarUsage.js create mode 100644 src/variables/editor/util/findVarUsage.js create mode 100644 src/variables/editor/util/propsLists.js rename src/variables/editor/{ => util}/testPages.js (61%) diff --git a/src/variables/editor/editor-test.js b/src/variables/editor/editor-test.js index 535cbb07..a72fd288 100644 --- a/src/variables/editor/editor-test.js +++ b/src/variables/editor/editor-test.js @@ -1,7 +1,7 @@ import { assert } from 'chai' import { VariableEditorVM } from './editor' -import { findVarUsage } from './findVarUsage' -import { testPages, expectedHtml } from './testPages' +import { findVarUsage } from './util/findVarUsage' +import { testPages, expectedGreedyHtml, expectedExplicitHtml } from './util/testPages' import 'steal-mocha' @@ -12,17 +12,24 @@ describe('', () => { beforeEach(() => { vm = new VariableEditorVM() }) - it('smoketest', () => { - assert.equal(vm.initialVariable, undefined, 'should start with no initial variables') + it('smoke test', () => { + assert.equal(vm.initialVariable, undefined, 'should start with no initial variable') }) }) describe('findVarUsage', () => { - it('gathers usage on Page, Field, and Button level property values using variables', function () { + it.only('gathers Greedy variable usage based on case insensitive variable name, aka "foo" and "foo123".', function () { const { pageCount, html } = findVarUsage('Foo', testPages) assert.equal(pageCount, 3, 'should return pageCount for variable usage across pages, fields, macros, buttons, and logic') - assert.equal(html, expectedHtml, 'should return html message for found locations') + assert.equal(html, expectedGreedyHtml, 'should return html message for found locations') + }) + + it('gathers Explicit variable usage based on case insensitive variable name, aka just "foo".', function () { + const { pageCount, html } = findVarUsage('Foo', testPages) + + assert.equal(pageCount, 2, 'should return pageCount for variable usage across pages, fields, macros, buttons, and logic') + assert.equal(html, expectedExplicitHtml, 'should return html message for found locations') }) }) }) diff --git a/src/variables/editor/editor.js b/src/variables/editor/editor.js index a7ffc273..21815e22 100644 --- a/src/variables/editor/editor.js +++ b/src/variables/editor/editor.js @@ -2,7 +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 { findVarUsage } from './findVarUsage' +import { findVarUsage } from './util/findVarUsage' export const VariableEditorVM = DefineMap.extend('VariableEditorVM', { /* @@ -117,6 +117,10 @@ export const VariableEditorVM = DefineMap.extend('VariableEditorVM', { } }, + useExplicitSearch: { + default: false + }, + emitVariable () { if (this._willEmit) { return @@ -153,14 +157,13 @@ export const VariableEditorVM = DefineMap.extend('VariableEditorVM', { onFindVarUsage () { // use the initially loaded name in case they've edited it in the form before checking usage - const variableName = this.initialVarName - const searchPages = this.guide.pages || {} - const { pageCount, html } = findVarUsage(variableName, searchPages) - - this.variableUsageHtml = html + const varName = this.initialVarName + const pages = this.guide.sortedPages || [] + const useExplicitSearch = this.useExplicitSearch - // courtesy return for testing - return { pageCount, html } + const results = findVarUsage(varName, pages, useExplicitSearch) + console.log('results', results) + return results } }) diff --git a/src/variables/editor/findVarUsage.js b/src/variables/editor/findVarUsage.js deleted file mode 100644 index 80acb515..00000000 --- a/src/variables/editor/findVarUsage.js +++ /dev/null @@ -1,95 +0,0 @@ -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' } -] - -export const findVarUsage = (varName, pages) => { // 2015-03-27 Search for variable or constant - let html = '' - let pageCount = 0 - let pageName - const lowerCaseVarName = varName.toLowerCase() - const mainRegexString = `\\(\\s*${lowerCaseVarName}\\s*\\)` - const macroRegexString = `\\%\\s*${lowerCaseVarName}\\s*\\%` - const logicRegexString = `\\[\\s*${lowerCaseVarName}\\s*\\]` - - // const regexString = `\\(\\s*${lowerCaseVarName}\\s*\\)|\\%\\s*${lowerCaseVarName}\\s*\\%|\\[\\s*${lowerCaseVarName}\\s*\\]` - const regexString = `${mainRegexString}|${macroRegexString}|${logicRegexString}` - const macroRegex = new RegExp(regexString, 'i') - - for (pageName in pages) { // Search text, buttons, help, fields and logic for variable name. - /** @type TPage */ - const where = [] // list where it's on this page - const page = pages[pageName] - - const findMatches = (searchTarget, usageItem) => { - // skip check if not string value to check - const prop = usageItem.key - if (!searchTarget[prop]) { return } - const testValue = searchTarget[prop].toLowerCase() - - if (usageItem.type === 'regex') { // check for macro matches, `%%someVar%%` - const matches = testValue.match(macroRegex) - if (matches && matches.length) { - where.push(usageItem.display) - } - } else if (usageItem.type === 'logic') { - if (testValue.indexOf(lowerCaseVarName) !== -1) { // check for logic usage (no macro syntax, `set someVar to "foo"`) - where.push(usageItem.display) - } - } else { - if (testValue === lowerCaseVarName) { // check for varName itself, `someVar` - where.push(usageItem.display) - } - } - } - - // check top level page properties - for (const entry of pageProps) { - findMatches(page, entry) - } - - // check all page fields - for (const field of page.fields) { - for (const entry of fieldProps) { - findMatches(field, entry) - } - } - - // check all buttons - for (const button of page.buttons) { - for (const entry of buttonProps) { - findMatches(button, entry) - } - } - - if (where.length > 0) { // If we found anything, we'll list the page and its location. - pageCount++ - html += ('
  • ' + page.name + '
  • ') - } - } - - return { pageCount, html: 'Used in ' + pageCount + ' pages' + '' } -} diff --git a/src/variables/editor/util/findVarUsage.js b/src/variables/editor/util/findVarUsage.js new file mode 100644 index 00000000..a0dd299c --- /dev/null +++ b/src/variables/editor/util/findVarUsage.js @@ -0,0 +1,72 @@ +import DefineMap from 'can-define/map/map' +import { pageProps, fieldProps, buttonProps } from './propsLists' + +const getRegEx = (varName, useExplicitSearch) => { + const lowerCaseVarName = varName.toLowerCase() + + const parens = `\\(${lowerCaseVarName}\\)` + const percent = `\\%${lowerCaseVarName}\\%` + const brackets = `\\[${lowerCaseVarName}\\]` + const noTrailingQuotes = `${lowerCaseVarName}(?!")` + const implicitPrep = `(? { // pages or fields or buttons + const foundMatches = { page: [], fields: [], buttons: [] } + // check top level page properties + for (const entry of pageProps) { + const prop = entry.key + const testValue = page[prop] + + const matches = testValue.match(regEx) + + if (matches && matches.length) { + foundMatches.page.push(entry.display) + } + } + + for (const field of page.fields) { + for (const entry of fieldProps) { + const prop = entry.key + const testValue = field[prop] + + const matches = testValue.match(regEx) + + if (matches && matches.length) { + foundMatches.fields.push(entry.display) + } + } + } + + for (const button of page.buttons) { + for (const entry of buttonProps) { + const prop = entry.key + const testValue = button[prop] + + const matches = testValue.match(regEx) + + if (matches && matches.length) { + foundMatches.buttons.push(entry.display) + } + } + } + + return foundMatches +} + +export const findVarUsage = (varName, pages, useExplicitSearch) => { + const regEx = getRegEx(varName, useExplicitSearch) + const matches = new DefineMap({}) + + for (const page of pages) { + matches[page.name] = getMatches(page, regEx) + } + + console.log('final matches', matches) + return matches +} diff --git a/src/variables/editor/util/propsLists.js b/src/variables/editor/util/propsLists.js new file mode 100644 index 00000000..d61c7b12 --- /dev/null +++ b/src/variables/editor/util/propsLists.js @@ -0,0 +1,27 @@ +export const pageProps = [ + { key: 'name', type: 'macro', display: 'Page Name' }, + { key: 'text', type: 'macro', display: 'Question Text' }, + { key: 'repeatVar', type: 'string', display: 'Counting Variable' }, + { key: 'outerLoopVar', type: 'string', display: 'Outer Loop Variable' }, + { key: 'learn', type: 'macro', display: 'LearnMore Prompt' }, + { key: 'help', type: 'macro', display: 'LearnMore Response' }, + { key: 'helpReader', type: 'macro', display: 'Video Transcript' }, + { key: 'codeBefore', type: 'logic', display: 'Before Logic' }, + { key: 'codeAfter', type: 'logic', display: 'After Logic' } +] + +export const fieldProps = [ + { key: 'label', type: 'macro', display: 'Field Label' }, + { key: 'name', type: 'string', display: 'Field Variable' }, + { key: 'value', type: 'macro', display: 'Field Default Value' }, + { key: 'invalidPrompt', type: 'macro', display: 'Field Custom Invalid Prompt' }, + { key: 'sample', type: 'macro', display: 'Field Sample Value' } +] + +export const buttonProps = [ + { key: 'label', type: 'macro', display: 'Button Label' }, + { key: 'name', type: 'string', display: 'Button Variable Name' }, + { key: 'value', type: 'macro', display: 'Button Default Value' }, + { key: 'repeatVar', type: 'string', display: 'Button Counting Variable' }, + { key: 'url', type: 'macro', display: 'Button URL' } +] diff --git a/src/variables/editor/testPages.js b/src/variables/editor/util/testPages.js similarity index 61% rename from src/variables/editor/testPages.js rename to src/variables/editor/util/testPages.js index a1fc3074..e1c73421 100644 --- a/src/variables/editor/testPages.js +++ b/src/variables/editor/util/testPages.js @@ -1,7 +1,7 @@ -export const testPages = { - '1-Introduction': { +export const testPages = [ + { name: '1-Introduction', - text: '

    This is the introduction. %%[foo]%%

    ', + text: '

    This is the introduction. Find this use of variable ->  %%[Foo]%%

    ', textCitation: '', textAudioURL: '', notes: '', @@ -14,16 +14,16 @@ export const testPages = { helpImageURL: '', helpAltText: '', helpVideoURL: '', - repeatVar: 'foo', - outerLoopVar: 'foo', - codeBefore: "set [foo] to 'bar'
    ", - codeAfter: "set [foo] to 'foo'
    ", + repeatVar: 'Foo', + outerLoopVar: 'Foo', + codeBefore: 'set [Foo] to "before"
    ', + codeAfter: 'set [Foo] to "after"
    ', codeCitation: '', fields: [ { type: 'text', label: 'Label', - name: 'foo', + name: 'Foo', value: '', required: false, invalidPrompt: '', @@ -41,11 +41,11 @@ export const testPages = { buttons: [ { label: 'Continue', - next: '2-Name', + next: '2-Name %%Foo%%', url: '', - name: 'foo', + name: 'Foo', value: '', - repeatVar: 'foo', + repeatVar: 'Foo', repeatVarSet: '', message: '' } @@ -56,11 +56,53 @@ export const testPages = { mapx: 60, mapy: 60, mapBranches: null, - xml: "

    This is the introduction. %%[foo]%%

    fooset [foo] to 'bar'
    set [foo] to 'foo'
    ", + xml: '

    This is the introduction. Find this use of variable ->  %%[Foo]%%

    Fooset [Foo] to "before"
    set [Foo] to "after"
    ', nested: false }, - '2-Name': { - name: '2-Name', + { + name: '1-Question 1', + text: '

    Text of my first question goes here.

    ', + textCitation: '', + textAudioURL: '', + notes: '', + learn: '', + help: '', + helpCitation: '', + helpMediaLabel: '', + helpAudioURL: '', + helpReader: '', + helpImageURL: '', + helpAltText: '', + helpVideoURL: '', + repeatVar: '', + outerLoopVar: '', + codeBefore: '', + codeAfter: '', + codeCitation: '', + fields: [], + buttons: [ + { + label: 'Save', + next: 'SUCCESS', + url: '', + name: '', + value: '', + repeatVar: '', + repeatVarSet: '', + message: '' + } + ], + step: 1, + type: 'A2J', + style: '', + mapx: 240, + mapy: 60, + mapBranches: null, + xml: '

    Text of my first question goes here.

    ', + nested: false + }, + { + name: '2-Name %%Foo%%', text: '

    Enter your name. 

    ', textCitation: '', textAudioURL: '', @@ -77,7 +119,7 @@ export const testPages = { repeatVar: '', outerLoopVar: '', codeBefore: '', - codeAfter: "set [foo123] to 'foo'
    ", + codeAfter: 'SET [Foo 123] to "second"
    ', codeCitation: '', fields: [ { @@ -150,10 +192,10 @@ export const testPages = { mapx: 60, mapy: 300, mapBranches: null, - xml: "

    Enter your name. 

    Client first name TEClient middle name TEClient last name TEset [foo123] to 'foo'
    ", + xml: '

    Enter your name. 

    Client first name TEClient middle name TEClient last name TESET [Foo 123] to "second"
    ', nested: false }, - '3-Avatar': { + { name: '3-Avatar', text: 'Choose your avatar.', textCitation: '', @@ -168,10 +210,10 @@ export const testPages = { helpImageURL: '', helpAltText: '', helpVideoURL: '', - repeatVar: '', - outerLoopVar: '', + repeatVar: 'Foo-42', + outerLoopVar: 'Foo-42', codeBefore: '', - codeAfter: "set [foo-42] to 'foo'
    ", + codeAfter: 'SET [Foo-42] TO "third"
    ', codeCitation: '', fields: [ { @@ -190,6 +232,23 @@ export const testPages = { listSrc: '', listData: '', sample: '' + }, + { + type: 'text', + label: 'Label', + name: 'Foo-42', + value: '', + required: false, + invalidPrompt: '', + invalidPromptAudio: '', + order: '', + min: '', + max: '', + calculator: false, + maxChars: '', + listSrc: '', + listData: '', + sample: '' } ], buttons: [ @@ -197,9 +256,9 @@ export const testPages = { label: 'Continue', next: '1-Question 1', url: '', - name: '', + name: 'Foo-42', value: '', - repeatVar: '', + repeatVar: 'Foo-42', repeatVarSet: '', message: '' } @@ -210,51 +269,10 @@ export const testPages = { mapx: 60, mapy: 540, mapBranches: null, - xml: "Choose your avatar.User Avatarset [foo-42] to 'foo'
    ", - nested: false - }, - '1-Question 1': { - name: '1-Question 1', - text: '

    Text of my first question goes here.

    ', - textCitation: '', - textAudioURL: '', - notes: '', - learn: '', - help: '', - helpCitation: '', - helpMediaLabel: '', - helpAudioURL: '', - helpReader: '', - helpImageURL: '', - helpAltText: '', - helpVideoURL: '', - repeatVar: '', - outerLoopVar: '', - codeBefore: '', - codeAfter: '', - codeCitation: '', - fields: [], - buttons: [ - { - label: 'Save', - next: 'SUCCESS', - url: '', - name: '', - value: '', - repeatVar: '', - repeatVarSet: '', - message: '' - } - ], - step: 1, - type: 'A2J', - style: '', - mapx: 240, - mapy: 60, - mapBranches: null, - xml: '

    Text of my first question goes here.

    ', + xml: 'Choose your avatar.User AvatarFoo-42SET [Foo-42] TO "third"
    ', nested: false } -} +] -export const expectedHtml = 'Used in 3 pages' +export const expectedGreedyHtml = 'Used in 3 pages' +export const expectedExplicitHtml = 'Used in 1 pages