bare-script 3.7.1 → 3.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bare.js +4 -4
- package/lib/include/args.bare +14 -14
- package/lib/include/dataTable.bare +5 -5
- package/lib/include/diff.bare +11 -11
- package/lib/include/forms.bare +28 -28
- package/lib/include/markdownUp.bare +15 -15
- package/lib/include/pager.bare +12 -12
- package/lib/include/unittest.bare +62 -62
- package/lib/include/unittestMock.bare +34 -34
- package/lib/model.js +44 -24
- package/lib/parser.js +68 -27
- package/lib/runtimeAsync.js +1 -1
- package/package.json +1 -1
|
@@ -15,8 +15,8 @@ include <diff.bare>
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
# Test statistics
|
|
18
|
-
unittestTests =
|
|
19
|
-
unittestWarnings =
|
|
18
|
+
unittestTests = {}
|
|
19
|
+
unittestWarnings = []
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
# $function: unittestRunTest
|
|
@@ -60,7 +60,7 @@ function unittestRunTestHelper(testName):
|
|
|
60
60
|
endif
|
|
61
61
|
|
|
62
62
|
# Add the unit test result array
|
|
63
|
-
testFailures =
|
|
63
|
+
testFailures = []
|
|
64
64
|
objectSet(unittestTests, testName, testFailures)
|
|
65
65
|
systemGlobalSet('unittestTestName', testName)
|
|
66
66
|
|
|
@@ -81,7 +81,7 @@ function unittestEqual(actual, expected, description):
|
|
|
81
81
|
|
|
82
82
|
# Add the test failure error lines
|
|
83
83
|
testFailures = objectGet(unittestTests, unittestTestName)
|
|
84
|
-
errorLines =
|
|
84
|
+
errorLines = [ \
|
|
85
85
|
'Equal:', \
|
|
86
86
|
'', \
|
|
87
87
|
'```', \
|
|
@@ -91,9 +91,9 @@ function unittestEqual(actual, expected, description):
|
|
|
91
91
|
'```', \
|
|
92
92
|
jsonStringify(expected), \
|
|
93
93
|
'```' \
|
|
94
|
-
|
|
94
|
+
]
|
|
95
95
|
if description:
|
|
96
|
-
arrayPush(testFailures, arrayExtend(
|
|
96
|
+
arrayPush(testFailures, arrayExtend([markdownEscape(description), ''], errorLines))
|
|
97
97
|
else:
|
|
98
98
|
arrayPush(testFailures, errorLines)
|
|
99
99
|
endif
|
|
@@ -115,8 +115,8 @@ function unittestDeepEqual(actual, expected, description):
|
|
|
115
115
|
endif
|
|
116
116
|
|
|
117
117
|
# Compute the difference lines
|
|
118
|
-
errorLines =
|
|
119
|
-
diffErrorLines =
|
|
118
|
+
errorLines = []
|
|
119
|
+
diffErrorLines = []
|
|
120
120
|
for diff in diffLines(actualJSON, expectedJSON):
|
|
121
121
|
diffType = objectGet(diff, 'type')
|
|
122
122
|
if diffType == 'Remove':
|
|
@@ -130,15 +130,15 @@ function unittestDeepEqual(actual, expected, description):
|
|
|
130
130
|
else:
|
|
131
131
|
# diffType == 'Identical'
|
|
132
132
|
if diffErrorLines:
|
|
133
|
-
arrayExtend(errorLines,
|
|
133
|
+
arrayExtend(errorLines, ['Deep-equal:', '', '```'])
|
|
134
134
|
arrayExtend(errorLines, diffErrorLines)
|
|
135
135
|
arrayPush(errorLines, '```')
|
|
136
|
-
diffErrorLines =
|
|
136
|
+
diffErrorLines = []
|
|
137
137
|
endif
|
|
138
138
|
endif
|
|
139
139
|
endfor
|
|
140
140
|
if diffErrorLines:
|
|
141
|
-
arrayExtend(errorLines,
|
|
141
|
+
arrayExtend(errorLines, ['Deep-equal:', '', '```'])
|
|
142
142
|
arrayExtend(errorLines, diffErrorLines)
|
|
143
143
|
arrayPush(errorLines, '```')
|
|
144
144
|
endif
|
|
@@ -146,7 +146,7 @@ function unittestDeepEqual(actual, expected, description):
|
|
|
146
146
|
# Add the test failure error lines
|
|
147
147
|
testFailures = objectGet(unittestTests, unittestTestName)
|
|
148
148
|
if description:
|
|
149
|
-
arrayPush(testFailures, arrayExtend(
|
|
149
|
+
arrayPush(testFailures, arrayExtend([markdownEscape(description), ''], errorLines))
|
|
150
150
|
else:
|
|
151
151
|
arrayPush(testFailures, errorLines)
|
|
152
152
|
endif
|
|
@@ -210,7 +210,7 @@ function unittestReport(options):
|
|
|
210
210
|
if testNameArg:
|
|
211
211
|
markdownPrint('', argsLink(unittestReportArguments, 'All tests', null, true))
|
|
212
212
|
else:
|
|
213
|
-
markdownPrint('', argsLink(unittestReportArguments, if(hideTests, 'Show', 'Hide') + ' tests',
|
|
213
|
+
markdownPrint('', argsLink(unittestReportArguments, if(hideTests, 'Show', 'Hide') + ' tests', {'hideTests': !hideTests}))
|
|
214
214
|
endif
|
|
215
215
|
endif
|
|
216
216
|
|
|
@@ -227,15 +227,15 @@ function unittestReport(options):
|
|
|
227
227
|
if testFailCount:
|
|
228
228
|
markdownPrint('', '## Failing Tests')
|
|
229
229
|
if hideTests && !isReport && !testNameArg:
|
|
230
|
-
markdownPrint('', argsLink(unittestReportArguments, 'Show',
|
|
230
|
+
markdownPrint('', argsLink(unittestReportArguments, 'Show', {'hideTests': false}))
|
|
231
231
|
endif
|
|
232
232
|
if !hideTests || testNameArg:
|
|
233
233
|
for testName in testNames:
|
|
234
234
|
testFailures = objectGet(unittestTests, testName)
|
|
235
235
|
if arrayLength(testFailures):
|
|
236
236
|
failureLink = if(isReport, testName, \
|
|
237
|
-
argsLink(unittestReportArguments, testName,
|
|
238
|
-
failureLines =
|
|
237
|
+
argsLink(unittestReportArguments, testName, {'test': testName}, false, '_top'))
|
|
238
|
+
failureLines = ['', failureLink + ' - FAIL']
|
|
239
239
|
for errorLines in testFailures:
|
|
240
240
|
for errorLine, ixErrorLine in errorLines:
|
|
241
241
|
if ixErrorLine == 0:
|
|
@@ -256,14 +256,14 @@ function unittestReport(options):
|
|
|
256
256
|
if testPassCount:
|
|
257
257
|
markdownPrint('', '## Passing Tests')
|
|
258
258
|
if hideTests && !isReport && !testNameArg:
|
|
259
|
-
markdownPrint('', argsLink(unittestReportArguments, 'Show',
|
|
259
|
+
markdownPrint('', argsLink(unittestReportArguments, 'Show', {'hideTests': false}))
|
|
260
260
|
endif
|
|
261
261
|
if !hideTests || testNameArg:
|
|
262
262
|
for testName in testNames:
|
|
263
263
|
testFailures = objectGet(unittestTests, testName)
|
|
264
264
|
if !arrayLength(testFailures):
|
|
265
265
|
testLink = if(isReport, testName, \
|
|
266
|
-
argsLink(unittestReportArguments, testName,
|
|
266
|
+
argsLink(unittestReportArguments, testName, {'test': testName}, false, '_top'))
|
|
267
267
|
markdownPrint('', testLink + ' - OK')
|
|
268
268
|
endif
|
|
269
269
|
endfor
|
|
@@ -294,18 +294,18 @@ function unittestReport(options):
|
|
|
294
294
|
for row in coverageData:
|
|
295
295
|
scriptName = objectGet(row, 'Script')
|
|
296
296
|
if !isReport && !stringStartsWith(scriptName, '**'):
|
|
297
|
-
objectSet(row, 'Script', argsLink(unittestReportArguments, scriptName,
|
|
297
|
+
objectSet(row, 'Script', argsLink(unittestReportArguments, scriptName, {"script": scriptName}, false, "_top"))
|
|
298
298
|
endif
|
|
299
299
|
objectSet(row, 'Coverage', numberToFixed(objectGet(row, 'Coverage'), 1) + '%')
|
|
300
300
|
endfor
|
|
301
|
-
markdownPrint('', dataTableMarkdown(coverageData,
|
|
302
|
-
'fields'
|
|
303
|
-
'formats'
|
|
304
|
-
'Statements'
|
|
305
|
-
'Missing'
|
|
306
|
-
'Coverage'
|
|
307
|
-
|
|
308
|
-
))
|
|
301
|
+
markdownPrint('', dataTableMarkdown(coverageData, { \
|
|
302
|
+
'fields': ['Script', 'Statements', 'Missing', 'Coverage'], \
|
|
303
|
+
'formats': { \
|
|
304
|
+
'Statements': {'align': 'right'}, \
|
|
305
|
+
'Missing': {'align': 'right'}, \
|
|
306
|
+
'Coverage': {'align': 'right'} \
|
|
307
|
+
} \
|
|
308
|
+
}))
|
|
309
309
|
else:
|
|
310
310
|
markdownPrint('', '*No data.*')
|
|
311
311
|
endif
|
|
@@ -317,12 +317,12 @@ endfunction
|
|
|
317
317
|
|
|
318
318
|
|
|
319
319
|
# unittestReport application arguments
|
|
320
|
-
unittestReportArguments = argsValidate(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
)
|
|
320
|
+
unittestReportArguments = argsValidate([ \
|
|
321
|
+
{'name': 'test', 'global': 'vUnittestTest'}, \
|
|
322
|
+
{'name': 'script', 'global': 'vUnittestScript'}, \
|
|
323
|
+
{'name': 'hideTests', 'global': 'vUnittestHideTests', 'type': 'bool', 'default': false}, \
|
|
324
|
+
{'name': 'report', 'global': 'vUnittestReport', 'type': 'bool', 'default': false} \
|
|
325
|
+
])
|
|
326
326
|
|
|
327
327
|
|
|
328
328
|
# Render the script coverage details page
|
|
@@ -331,7 +331,7 @@ function unittestReportScript(scriptName):
|
|
|
331
331
|
title = scriptName + ' Coverage'
|
|
332
332
|
documentSetTitle(title)
|
|
333
333
|
markdownPrint( \
|
|
334
|
-
argsLink(unittestReportArguments, 'Back',
|
|
334
|
+
argsLink(unittestReportArguments, 'Back', {'script': null}, false, '_top'), \
|
|
335
335
|
'', \
|
|
336
336
|
'# ' + markdownEscape(title) \
|
|
337
337
|
)
|
|
@@ -349,7 +349,7 @@ function unittestReportScript(scriptName):
|
|
|
349
349
|
scriptLines = objectGet(script, 'scriptLines')
|
|
350
350
|
|
|
351
351
|
# Mark the statements covered or not-covered
|
|
352
|
-
lineColors =
|
|
352
|
+
lineColors = {}
|
|
353
353
|
statements = unittestScriptStatements(script)
|
|
354
354
|
for statement in statements:
|
|
355
355
|
statementKey = arrayGet(objectKeys(statement), 0)
|
|
@@ -369,9 +369,9 @@ function unittestReportScript(scriptName):
|
|
|
369
369
|
endfor
|
|
370
370
|
|
|
371
371
|
# Generate the code coverage details
|
|
372
|
-
codeLineElements =
|
|
373
|
-
codeElements =
|
|
374
|
-
currentLines =
|
|
372
|
+
codeLineElements = []
|
|
373
|
+
codeElements = {'html': 'pre', 'elem': codeLineElements}
|
|
374
|
+
currentLines = []
|
|
375
375
|
currentColor = null
|
|
376
376
|
for line, ixLine in scriptLines:
|
|
377
377
|
# Accumulate lines of the same color
|
|
@@ -383,7 +383,7 @@ function unittestReportScript(scriptName):
|
|
|
383
383
|
|
|
384
384
|
# Render the current lines
|
|
385
385
|
unittestReportScriptLines(codeLineElements, currentLines, currentColor)
|
|
386
|
-
currentLines =
|
|
386
|
+
currentLines = [line]
|
|
387
387
|
currentColor = lineColor
|
|
388
388
|
endfor
|
|
389
389
|
unittestReportScriptLines(codeLineElements, currentLines, currentColor, true)
|
|
@@ -395,13 +395,13 @@ function unittestReportScriptLines(codeLineElements, lines, color, noNewline):
|
|
|
395
395
|
if lines:
|
|
396
396
|
linesStr = arrayJoin(lines, stringFromCharCode(10)) + if(noNewline, '', stringFromCharCode(10))
|
|
397
397
|
if !color:
|
|
398
|
-
arrayPush(codeLineElements,
|
|
398
|
+
arrayPush(codeLineElements, {'text': linesStr})
|
|
399
399
|
else:
|
|
400
|
-
arrayPush(codeLineElements,
|
|
401
|
-
'html'
|
|
402
|
-
'attr'
|
|
403
|
-
'elem'
|
|
404
|
-
)
|
|
400
|
+
arrayPush(codeLineElements, { \
|
|
401
|
+
'html': 'span', \
|
|
402
|
+
'attr': {'style': 'display: block; background-color: ' + color}, \
|
|
403
|
+
'elem': {'text': linesStr} \
|
|
404
|
+
})
|
|
405
405
|
endif
|
|
406
406
|
endif
|
|
407
407
|
endfunction
|
|
@@ -409,14 +409,14 @@ endfunction
|
|
|
409
409
|
|
|
410
410
|
# Get the coverage data table - columns are "Script", "Statement", "Covered", and "Percent"
|
|
411
411
|
function unittestCoverageData(coverageExclude):
|
|
412
|
-
data =
|
|
412
|
+
data = []
|
|
413
413
|
|
|
414
414
|
# Get the global coverage object
|
|
415
415
|
coverage = coverageGlobalGet()
|
|
416
416
|
if coverage:
|
|
417
417
|
# Get the script names with coverage data
|
|
418
418
|
scripts = objectGet(coverage, 'scripts')
|
|
419
|
-
scriptNames = if(scripts, arraySort(objectKeys(scripts)),
|
|
419
|
+
scriptNames = if(scripts, arraySort(objectKeys(scripts)), [])
|
|
420
420
|
|
|
421
421
|
# Compute script statement coverage
|
|
422
422
|
totalStatements = 0
|
|
@@ -458,25 +458,25 @@ function unittestCoverageData(coverageExclude):
|
|
|
458
458
|
|
|
459
459
|
# Add the script coverage data row
|
|
460
460
|
coveragePercent = if(statementCount, 100 * (statementCount - missingCount) / statementCount, 100)
|
|
461
|
-
arrayPush(data,
|
|
462
|
-
'Script'
|
|
463
|
-
'Statements'
|
|
464
|
-
'Missing'
|
|
465
|
-
'Coverage'
|
|
466
|
-
'CoverageStr'
|
|
467
|
-
)
|
|
461
|
+
arrayPush(data, { \
|
|
462
|
+
'Script': scriptName, \
|
|
463
|
+
'Statements': statementCount, \
|
|
464
|
+
'Missing': missingCount, \
|
|
465
|
+
'Coverage': coveragePercent, \
|
|
466
|
+
'CoverageStr': numberToFixed(coveragePercent, 1) + '%' \
|
|
467
|
+
})
|
|
468
468
|
endfor
|
|
469
469
|
|
|
470
470
|
# Add the coverage totals data row
|
|
471
471
|
if scriptNames:
|
|
472
472
|
coveragePercent = if(totalStatements, 100 * (totalStatements - totalMissing) / totalStatements, 100)
|
|
473
|
-
arrayPush(data,
|
|
474
|
-
'Script'
|
|
475
|
-
'Statements'
|
|
476
|
-
'Missing'
|
|
477
|
-
'Coverage'
|
|
478
|
-
'CoverageStr'
|
|
479
|
-
)
|
|
473
|
+
arrayPush(data, { \
|
|
474
|
+
'Script': '**Total**', \
|
|
475
|
+
'Statements': totalStatements, \
|
|
476
|
+
'Missing': totalMissing, \
|
|
477
|
+
'Coverage': coveragePercent, \
|
|
478
|
+
'CoverageStr': numberToFixed(coveragePercent, 1) + '%' \
|
|
479
|
+
})
|
|
480
480
|
endif
|
|
481
481
|
endif
|
|
482
482
|
|
|
@@ -486,7 +486,7 @@ endfunction
|
|
|
486
486
|
|
|
487
487
|
# Get a script's array of statements
|
|
488
488
|
function unittestScriptStatements(script):
|
|
489
|
-
statements =
|
|
489
|
+
statements = []
|
|
490
490
|
|
|
491
491
|
# Add the top-level script statements
|
|
492
492
|
for statement in objectGet(script, 'statements'):
|
|
@@ -18,16 +18,16 @@ unittestMockWindowWidth = 1024
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
# The mocked MarkdownUp state
|
|
21
|
-
unittestMockCalls =
|
|
22
|
-
unittestMockFunctions =
|
|
23
|
-
unittestMockStateDefault =
|
|
24
|
-
'drawingFontSizePx'
|
|
25
|
-
'drawingHeight'
|
|
26
|
-
'drawingWidth'
|
|
27
|
-
'localStorage'
|
|
28
|
-
'sessionStorage'
|
|
29
|
-
'windowClipboard'
|
|
30
|
-
|
|
21
|
+
unittestMockCalls = []
|
|
22
|
+
unittestMockFunctions = {}
|
|
23
|
+
unittestMockStateDefault = { \
|
|
24
|
+
'drawingFontSizePx': unittestMockDefaultFontSizePx, \
|
|
25
|
+
'drawingHeight': 480, \
|
|
26
|
+
'drawingWidth': 640, \
|
|
27
|
+
'localStorage': {}, \
|
|
28
|
+
'sessionStorage': {}, \
|
|
29
|
+
'windowClipboard': '' \
|
|
30
|
+
}
|
|
31
31
|
unittestMockState = objectCopy(unittestMockStateDefault)
|
|
32
32
|
|
|
33
33
|
|
|
@@ -145,7 +145,7 @@ endfunction
|
|
|
145
145
|
|
|
146
146
|
function unittestMockGeneric(funcName, args...):
|
|
147
147
|
# Record the mocked function call
|
|
148
|
-
arrayPush(unittestMockCalls,
|
|
148
|
+
arrayPush(unittestMockCalls, [funcName, args])
|
|
149
149
|
endfunction
|
|
150
150
|
|
|
151
151
|
|
|
@@ -161,8 +161,8 @@ function unittestMockEnd():
|
|
|
161
161
|
|
|
162
162
|
# Reset the mock state
|
|
163
163
|
mockCalls = unittestMockCalls
|
|
164
|
-
systemGlobalSet('unittestMockCalls',
|
|
165
|
-
systemGlobalSet('unittestMockFunctions',
|
|
164
|
+
systemGlobalSet('unittestMockCalls', [])
|
|
165
|
+
systemGlobalSet('unittestMockFunctions', {})
|
|
166
166
|
systemGlobalSet('unittestMockState', objectCopy(unittestMockStateDefault))
|
|
167
167
|
|
|
168
168
|
return mockCalls
|
|
@@ -183,7 +183,7 @@ function unittestMock_documentInputValue(data, args...):
|
|
|
183
183
|
id = arrayGet(args, 0)
|
|
184
184
|
|
|
185
185
|
# Record the mocked function call
|
|
186
|
-
arrayPush(unittestMockCalls,
|
|
186
|
+
arrayPush(unittestMockCalls, ['documentInputValue', args])
|
|
187
187
|
|
|
188
188
|
# Return the mocked documentInputValue response
|
|
189
189
|
return if(data != null, objectGet(data, id))
|
|
@@ -210,7 +210,7 @@ function unittestMock_drawNew(args...):
|
|
|
210
210
|
height = arrayGet(args, 1)
|
|
211
211
|
|
|
212
212
|
# Record the mocked function call
|
|
213
|
-
arrayPush(unittestMockCalls,
|
|
213
|
+
arrayPush(unittestMockCalls, ['drawNew', args])
|
|
214
214
|
|
|
215
215
|
# Update the mock state
|
|
216
216
|
objectSet(unittestMockState, 'drawingWidth', width)
|
|
@@ -230,7 +230,7 @@ function unittestMock_drawTextStyle(args...):
|
|
|
230
230
|
fontSizePx = arrayGet(args, 0)
|
|
231
231
|
|
|
232
232
|
# Record the mocked function call
|
|
233
|
-
arrayPush(unittestMockCalls,
|
|
233
|
+
arrayPush(unittestMockCalls, ['drawTextStyle', args])
|
|
234
234
|
|
|
235
235
|
# Update the mock state
|
|
236
236
|
objectSet(unittestMockState, 'drawingFontSizePx', if(fontSizePx != null, fontSizePx, unittestMockDefaultFontSizePx))
|
|
@@ -254,10 +254,10 @@ endfunction
|
|
|
254
254
|
|
|
255
255
|
function unittestMock_localStorageClear(args...):
|
|
256
256
|
# Record the mocked function call
|
|
257
|
-
arrayPush(unittestMockCalls,
|
|
257
|
+
arrayPush(unittestMockCalls, ['localStorageClear', args])
|
|
258
258
|
|
|
259
259
|
# Update the mock state
|
|
260
|
-
objectSet(unittestMockState, 'localStorage',
|
|
260
|
+
objectSet(unittestMockState, 'localStorage', {})
|
|
261
261
|
endfunction
|
|
262
262
|
|
|
263
263
|
|
|
@@ -270,7 +270,7 @@ function unittestMock_localStorageRemove(args...):
|
|
|
270
270
|
key = arrayGet(args, 0)
|
|
271
271
|
|
|
272
272
|
# Record the mocked function call
|
|
273
|
-
arrayPush(unittestMockCalls,
|
|
273
|
+
arrayPush(unittestMockCalls, ['localStorageRemove', args])
|
|
274
274
|
|
|
275
275
|
# Update the mock state
|
|
276
276
|
objectDelete(objectGet(unittestMockState, 'localStorage'), key)
|
|
@@ -282,7 +282,7 @@ function unittestMock_localStorageSet(args...):
|
|
|
282
282
|
value = arrayGet(args, 1)
|
|
283
283
|
|
|
284
284
|
# Record the mocked function call
|
|
285
|
-
arrayPush(unittestMockCalls,
|
|
285
|
+
arrayPush(unittestMockCalls, ['localStorageSet', args])
|
|
286
286
|
|
|
287
287
|
# Update the mock state
|
|
288
288
|
objectSet(objectGet(unittestMockState, 'localStorage'), key, value)
|
|
@@ -317,7 +317,7 @@ unittestMock_markdownHeaderId_dash = regexNew('[^a-z0-9]+')
|
|
|
317
317
|
|
|
318
318
|
function unittestMock_markdownParse(data, args...):
|
|
319
319
|
# Record the mocked function call
|
|
320
|
-
arrayPush(unittestMockCalls,
|
|
320
|
+
arrayPush(unittestMockCalls, ['markdownParse', args])
|
|
321
321
|
|
|
322
322
|
# Return the mocked markdownParse response
|
|
323
323
|
return if(data != null, arrayShift(data))
|
|
@@ -326,7 +326,7 @@ endfunction
|
|
|
326
326
|
|
|
327
327
|
function unittestMock_markdownTitle(data, args...):
|
|
328
328
|
# Record the mocked function call
|
|
329
|
-
arrayPush(unittestMockCalls,
|
|
329
|
+
arrayPush(unittestMockCalls, ['markdownTitle', args])
|
|
330
330
|
|
|
331
331
|
# Return the mocked markdownTitle response
|
|
332
332
|
return if(data != null, arrayShift(data))
|
|
@@ -344,11 +344,11 @@ function unittestMock_schemaElements(types, typeName):
|
|
|
344
344
|
if userTypeKey == 'struct' && objectGet(objectGet(userType, 'struct'), 'union'):
|
|
345
345
|
userTypeKey = 'union'
|
|
346
346
|
endif
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
347
|
+
return [ \
|
|
348
|
+
[ \
|
|
349
|
+
{'html': 'h1', 'elem': {'text': userTypeKey + ' ' + typeName}} \
|
|
350
|
+
] \
|
|
351
|
+
]
|
|
352
352
|
endfunction
|
|
353
353
|
|
|
354
354
|
|
|
@@ -359,10 +359,10 @@ endfunction
|
|
|
359
359
|
|
|
360
360
|
function unittestMock_sessionStorageClear(args...):
|
|
361
361
|
# Record the mocked function call
|
|
362
|
-
arrayPush(unittestMockCalls,
|
|
362
|
+
arrayPush(unittestMockCalls, ['sessionStorageClear', args])
|
|
363
363
|
|
|
364
364
|
# Update the mock state
|
|
365
|
-
objectSet(unittestMockState, 'sessionStorage',
|
|
365
|
+
objectSet(unittestMockState, 'sessionStorage', {})
|
|
366
366
|
endfunction
|
|
367
367
|
|
|
368
368
|
|
|
@@ -375,7 +375,7 @@ function unittestMock_sessionStorageRemove(args...):
|
|
|
375
375
|
key = arrayGet(args, 0)
|
|
376
376
|
|
|
377
377
|
# Record the mocked function call
|
|
378
|
-
arrayPush(unittestMockCalls,
|
|
378
|
+
arrayPush(unittestMockCalls, ['sessionStorageRemove', args])
|
|
379
379
|
|
|
380
380
|
# Update the mock state
|
|
381
381
|
objectDelete(objectGet(unittestMockState, 'sessionStorage'), key)
|
|
@@ -387,7 +387,7 @@ function unittestMock_sessionStorageSet(args...):
|
|
|
387
387
|
value = arrayGet(args, 1)
|
|
388
388
|
|
|
389
389
|
# Record the mocked function call
|
|
390
|
-
arrayPush(unittestMockCalls,
|
|
390
|
+
arrayPush(unittestMockCalls, ['sessionStorageSet', args])
|
|
391
391
|
|
|
392
392
|
# Update the mock state
|
|
393
393
|
objectSet(objectGet(unittestMockState, 'sessionStorage'), key, value)
|
|
@@ -403,12 +403,12 @@ function unittestMock_systemFetch(data, args...):
|
|
|
403
403
|
url = arrayGet(args, 0)
|
|
404
404
|
|
|
405
405
|
# Record the mocked function call
|
|
406
|
-
arrayPush(unittestMockCalls,
|
|
406
|
+
arrayPush(unittestMockCalls, ['systemFetch', args])
|
|
407
407
|
|
|
408
408
|
# Array of URLs?
|
|
409
409
|
urlType = systemType(url)
|
|
410
410
|
if urlType == 'array':
|
|
411
|
-
result =
|
|
411
|
+
result = []
|
|
412
412
|
for urlItem in url:
|
|
413
413
|
urlActual = if(systemType(urlItem) == 'object', objectGet(urlItem, 'url'), urlItem)
|
|
414
414
|
arrayPush(result, if(data != null, objectGet(data, urlActual)))
|
|
@@ -446,7 +446,7 @@ function unittestMock_windowClipboardWrite(args...):
|
|
|
446
446
|
text = arrayGet(args, 0)
|
|
447
447
|
|
|
448
448
|
# Record the mocked function call
|
|
449
|
-
arrayPush(unittestMockCalls,
|
|
449
|
+
arrayPush(unittestMockCalls, ['windowClipboardWrite', args])
|
|
450
450
|
|
|
451
451
|
# Update the mock state
|
|
452
452
|
objectSet(unittestMockState, 'windowClipboard', text)
|
package/lib/model.js
CHANGED
|
@@ -324,21 +324,20 @@ export function validateExpression(expr) {
|
|
|
324
324
|
*/
|
|
325
325
|
export function lintScript(script) {
|
|
326
326
|
const warnings = [];
|
|
327
|
+
const {statements} = script;
|
|
327
328
|
|
|
328
329
|
// Empty script?
|
|
329
|
-
if (
|
|
330
|
-
warnings
|
|
330
|
+
if (statements.length === 0) {
|
|
331
|
+
lintScriptWarning(warnings, script, null, 'Empty script');
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
// Variable used before assignment?
|
|
334
335
|
const varAssigns = {};
|
|
335
336
|
const varUses = {};
|
|
336
|
-
getVariableAssignmentsAndUses(
|
|
337
|
+
getVariableAssignmentsAndUses(statements, varAssigns, varUses);
|
|
337
338
|
for (const varName of Object.keys(varAssigns)) {
|
|
338
339
|
if (varName in varUses && varUses[varName] <= varAssigns[varName]) {
|
|
339
|
-
warnings
|
|
340
|
-
`Global variable "${varName}" used (index ${varUses[varName]}) before assignment (index ${varAssigns[varName]})`
|
|
341
|
-
);
|
|
340
|
+
lintScriptWarning(warnings, script, statements[varUses[varName]], `Global variable "${varName}" used before assignment`);
|
|
342
341
|
}
|
|
343
342
|
}
|
|
344
343
|
|
|
@@ -346,14 +345,14 @@ export function lintScript(script) {
|
|
|
346
345
|
const functionsDefined = {};
|
|
347
346
|
const labelsDefined = {};
|
|
348
347
|
const labelsUsed = {};
|
|
349
|
-
for (const [ixStatement, statement] of
|
|
348
|
+
for (const [ixStatement, statement] of statements.entries()) {
|
|
350
349
|
const [statementKey] = Object.keys(statement);
|
|
351
350
|
|
|
352
351
|
// Function definition checks
|
|
353
352
|
if (statementKey === 'function') {
|
|
354
353
|
// Function redefinition?
|
|
355
354
|
if (statement.function.name in functionsDefined) {
|
|
356
|
-
warnings
|
|
355
|
+
lintScriptWarning(warnings, script, statement, `Redefinition of function "${statement.function.name}"`);
|
|
357
356
|
} else {
|
|
358
357
|
functionsDefined[statement.function.name] = ixStatement;
|
|
359
358
|
}
|
|
@@ -369,9 +368,9 @@ export function lintScript(script) {
|
|
|
369
368
|
continue;
|
|
370
369
|
}
|
|
371
370
|
if (varName in fnVarUses && fnVarUses[varName] <= fnVarAssigns[varName]) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
371
|
+
lintScriptWarning(
|
|
372
|
+
warnings, script, statement,
|
|
373
|
+
`Variable "${varName}" of function "${statement.function.name}" used before assignment`
|
|
375
374
|
);
|
|
376
375
|
}
|
|
377
376
|
}
|
|
@@ -379,8 +378,9 @@ export function lintScript(script) {
|
|
|
379
378
|
// Unused variables?
|
|
380
379
|
for (const varName of Object.keys(fnVarAssigns)) {
|
|
381
380
|
if (!(varName in fnVarUses)) {
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
lintScriptWarning(
|
|
382
|
+
warnings, script, statement,
|
|
383
|
+
`Unused variable "${varName}" defined in function "${statement.function.name}"`
|
|
384
384
|
);
|
|
385
385
|
}
|
|
386
386
|
}
|
|
@@ -391,13 +391,17 @@ export function lintScript(script) {
|
|
|
391
391
|
for (const arg of args) {
|
|
392
392
|
// Duplicate argument?
|
|
393
393
|
if (argsDefined.has(arg)) {
|
|
394
|
-
|
|
394
|
+
lintScriptWarning(
|
|
395
|
+
warnings, script, statement, `Duplicate argument "${arg}" of function "${statement.function.name}"`
|
|
396
|
+
);
|
|
395
397
|
} else {
|
|
396
398
|
argsDefined.add(arg);
|
|
397
399
|
|
|
398
400
|
// Unused argument?
|
|
399
401
|
if (!(arg in fnVarUses)) {
|
|
400
|
-
|
|
402
|
+
lintScriptWarning(
|
|
403
|
+
warnings, script, statement, `Unused argument "${arg}" of function "${statement.function.name}"`
|
|
404
|
+
);
|
|
401
405
|
}
|
|
402
406
|
}
|
|
403
407
|
}
|
|
@@ -413,7 +417,7 @@ export function lintScript(script) {
|
|
|
413
417
|
if (fnStatementKey === 'expr') {
|
|
414
418
|
// Pointless function expression statement?
|
|
415
419
|
if (!('name' in fnStatement.expr) && isPointlessExpression(fnStatement.expr.expr)) {
|
|
416
|
-
warnings
|
|
420
|
+
lintScriptWarning(warnings, script, statement, `Pointless statement in function "${statement.function.name}"`);
|
|
417
421
|
}
|
|
418
422
|
|
|
419
423
|
// Function label statement checks
|
|
@@ -421,8 +425,9 @@ export function lintScript(script) {
|
|
|
421
425
|
// Label redefinition?
|
|
422
426
|
const fnStatementLabel = fnStatement.label.name;
|
|
423
427
|
if (fnStatementLabel in fnLabelsDefined) {
|
|
424
|
-
|
|
425
|
-
|
|
428
|
+
lintScriptWarning(
|
|
429
|
+
warnings, script, statement,
|
|
430
|
+
`Redefinition of label "${fnStatementLabel}" in function "${statement.function.name}"`
|
|
426
431
|
);
|
|
427
432
|
} else {
|
|
428
433
|
fnLabelsDefined[fnStatementLabel] = ixFnStatement;
|
|
@@ -439,14 +444,14 @@ export function lintScript(script) {
|
|
|
439
444
|
// Unused function labels?
|
|
440
445
|
for (const label of Object.keys(fnLabelsDefined)) {
|
|
441
446
|
if (!(label in fnLabelsUsed)) {
|
|
442
|
-
warnings
|
|
447
|
+
lintScriptWarning(warnings, script, statement, `Unused label "${label}" in function "${statement.function.name}"`);
|
|
443
448
|
}
|
|
444
449
|
}
|
|
445
450
|
|
|
446
451
|
// Unknown function labels?
|
|
447
452
|
for (const label of Object.keys(fnLabelsUsed)) {
|
|
448
453
|
if (!(label in fnLabelsDefined)) {
|
|
449
|
-
warnings
|
|
454
|
+
lintScriptWarning(warnings, script, statement, `Unknown label "${label}" in function "${statement.function.name}"`);
|
|
450
455
|
}
|
|
451
456
|
}
|
|
452
457
|
|
|
@@ -454,7 +459,7 @@ export function lintScript(script) {
|
|
|
454
459
|
} else if (statementKey === 'expr') {
|
|
455
460
|
// Pointless global expression statement?
|
|
456
461
|
if (!('name' in statement.expr) && isPointlessExpression(statement.expr.expr)) {
|
|
457
|
-
warnings
|
|
462
|
+
lintScriptWarning(warnings, script, statement, 'Pointless global statement');
|
|
458
463
|
}
|
|
459
464
|
|
|
460
465
|
// Global label statement checks
|
|
@@ -462,7 +467,7 @@ export function lintScript(script) {
|
|
|
462
467
|
// Label redefinition?
|
|
463
468
|
const statementLabel = statement.label.name;
|
|
464
469
|
if (statementLabel in labelsDefined) {
|
|
465
|
-
warnings
|
|
470
|
+
lintScriptWarning(warnings, script, statement, `Redefinition of global label "${statementLabel}"`);
|
|
466
471
|
} else {
|
|
467
472
|
labelsDefined[statementLabel] = ixStatement;
|
|
468
473
|
}
|
|
@@ -478,14 +483,14 @@ export function lintScript(script) {
|
|
|
478
483
|
// Unused global labels?
|
|
479
484
|
for (const label of Object.keys(labelsDefined)) {
|
|
480
485
|
if (!(label in labelsUsed)) {
|
|
481
|
-
warnings
|
|
486
|
+
lintScriptWarning(warnings, script, statements[labelsDefined[label]], `Unused global label "${label}"`);
|
|
482
487
|
}
|
|
483
488
|
}
|
|
484
489
|
|
|
485
490
|
// Unknown global labels?
|
|
486
491
|
for (const label of Object.keys(labelsUsed)) {
|
|
487
492
|
if (!(label in labelsDefined)) {
|
|
488
|
-
warnings
|
|
493
|
+
lintScriptWarning(warnings, script, statements[labelsUsed[label]], `Unknown global label "${label}"`);
|
|
489
494
|
}
|
|
490
495
|
}
|
|
491
496
|
|
|
@@ -493,6 +498,21 @@ export function lintScript(script) {
|
|
|
493
498
|
}
|
|
494
499
|
|
|
495
500
|
|
|
501
|
+
// Helper to format static analysis warnings
|
|
502
|
+
function lintScriptWarning(warnings, script, statement, message) {
|
|
503
|
+
let warning;
|
|
504
|
+
if (script && statement) {
|
|
505
|
+
const [statementKey] = Object.keys(statement);
|
|
506
|
+
const scriptName = script.scriptName ?? '';
|
|
507
|
+
const lineno = statement[statementKey].lineNumber ?? '';
|
|
508
|
+
warning = (scriptName || lineno) ? `${scriptName}:${lineno}: ${message}` : message;
|
|
509
|
+
} else {
|
|
510
|
+
warning = message;
|
|
511
|
+
}
|
|
512
|
+
warnings.push(warning);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
496
516
|
// Helper function to determine if an expression statement's expression is pointless
|
|
497
517
|
function isPointlessExpression(expr) {
|
|
498
518
|
const [exprKey] = Object.keys(expr);
|