Skip to content
This repository was archived by the owner on Dec 17, 2024. It is now read-only.

Commit 817bbcd

Browse files
committed
fix test glitch in tab completion test
Fixes #697
1 parent 9e98398 commit 817bbcd

File tree

7 files changed

+117
-35
lines changed

7 files changed

+117
-35
lines changed

app/content/js/repl.js

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,58 @@ self.pexec = (command, execOptions) => self.exec(command, Object.assign({ echo:
449449

450450
const patterns = {
451451
commentLine: /\s*#.*$/,
452-
split: /(?:[^\s"']+|["'][^"']*["'])+/g,
453-
quotes: /^"(.*)"$/g
452+
whitespace: /\s/
453+
}
454+
const split = (str, removeOuterQuotes=true) => {
455+
const A = [],
456+
stack = []
457+
458+
let cur = ''
459+
460+
for (let idx = 0; idx < str.length; idx++) {
461+
const char = str.charAt(idx)
462+
463+
if (stack.length === 0 && char.match(patterns.whitespace)) {
464+
if (cur.length > 0) {
465+
A.push(cur)
466+
cur = ''
467+
}
468+
continue
469+
}
470+
471+
if (char === '\'' || char === '"') {
472+
const last = stack.length > 0 && stack[stack.length - 1]
473+
474+
if (char === last) {
475+
// found matching close quote
476+
stack.pop()
477+
478+
if (stack.length > 0 || !removeOuterQuotes) {
479+
// add the outer quotes?
480+
cur += char
481+
}
482+
483+
} else {
484+
// found open quote
485+
if (stack.length > 0 || !removeOuterQuotes) {
486+
// add the outer quotes?
487+
cur += char
488+
}
489+
490+
stack.push(char)
491+
}
492+
} else {
493+
// not a quote
494+
cur += char
495+
}
496+
}
497+
498+
if (cur.length > 0) {
499+
A.push(cur)
500+
}
501+
502+
return A
454503
}
455-
const split = str => str.match(patterns.split).map(s => s.replace(patterns.quotes, '$1'))
456504
self.split = split
457505

458506
/** an empty promise, for blank lines */
@@ -531,6 +579,8 @@ self.exec = (commandUntrimmed, execOptions) => {
531579
}
532580

533581
const argv = split(command)
582+
debug('split', command, argv)
583+
534584
if (argv.length === 0) {
535585
if (block) {
536586
ui.setStatus(block, 'valid-response')
@@ -613,7 +663,7 @@ self.exec = (commandUntrimmed, execOptions) => {
613663
debug('usage', usage, evaluator)
614664

615665
if (usage && usage.strict) { // strict: command wants *us* to enforce conformance
616-
// required and otional parameters
666+
// required and optional parameters
617667
const { strict:cmd, required=[], oneof=[], optional:_optional=[] } = usage,
618668
optLikeOneOfs = oneof.filter(({name}) => name.charAt(0) === '-'), // some one-ofs might be of the form --foo
619669
positionalConsumers = _optional.filter(({name, alias, consumesPositional}) => consumesPositional && (parsedOptions[unflag(name)] || parsedOptions[unflag(alias)])),

app/content/js/usage-error.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ const format = (message, options={}) => {
328328
}
329329

330330
// fields of the row model
331-
debug('row', rowData)
331+
// debug('row', rowData)
332332
const { commandPrefix, command=commandPrefix, name=command, label=name,
333333
alias, numeric, aliases=[alias], hidden=false, advanced=false,
334334
available,

app/plugins/openwhisk-extensions/actions/invoke.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,17 @@ module.exports = (commandTree, require) => {
128128
asyncInvoke = doAsync(rawInvoke.$) // ... and for async
129129

130130
wsk.synonyms('actions').forEach(syn => {
131-
commandTree.listen(`/wsk/${syn}/invoke`, syncInvoke, docs(syn))
132-
commandTree.listen(`/wsk/${syn}/async`, asyncInvoke, { usage: 'Invoke an action asynchronously' })
131+
const invokeOpts = docs(syn),
132+
asyncOpts = {
133+
usage: Object.assign({}, invokeOpts.usage,
134+
{ command: 'async',
135+
strict: 'async',
136+
title: 'Async Invoke',
137+
header: 'Invoke an action asynchronously'
138+
})
139+
}
140+
141+
commandTree.listen(`/wsk/${syn}/invoke`, syncInvoke, invokeOpts)
142+
commandTree.listen(`/wsk/${syn}/async`, asyncInvoke, asyncOpts)
133143
})
134144
}

app/plugins/ui/commands/shell.js

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
*
2121
*/
2222

23-
const shell = require('shelljs')
23+
const debug = require('debug')('shell plugin'),
24+
shell = require('shelljs')
2425

2526
const doShell = (argv, options, execOptions) => new Promise((resolve, reject) => {
2627
if (argv.length < 2) {
2728
reject('Please provide a bash command')
2829
}
2930

31+
debug('argv', argv)
32+
3033
const cmd = argv[1]
3134
if (shell[cmd]) {
3235
const args = argv.slice(2)
@@ -41,11 +44,11 @@ const doShell = (argv, options, execOptions) => new Promise((resolve, reject) =>
4144
args[0] = process.env.OLDPWD
4245
}
4346

44-
if (!args.find(arg => arg.charAt(0) === '-')) {
47+
if (!args.find(arg => arg.charAt(0) === '-') && cmd !== 'ls') {
4548
// shelljs doesn't like dash args
4649
// otherwise, shelljs has a built-in handler for this
4750

48-
console.log(`shell.internal: ${cmd}`)
51+
debug('using internal shelljs', cmd, args)
4952

5053
const output = shell[cmd](args)
5154
if (cmd === 'cd') {
@@ -76,7 +79,7 @@ const doShell = (argv, options, execOptions) => new Promise((resolve, reject) =>
7679
//
7780

7881
const cmdLine = argv.slice(1).join(' ')
79-
console.log(`shell.exec: ${cmdLine}`)
82+
debug('cmdline', cmdLine)
8083
const proc = shell.exec(cmdLine, {async: true, silent: true, env: process.env})
8184

8285
// accumulate doms from the output of the subcommand
@@ -156,25 +159,25 @@ const usage = {
156159
module.exports = commandTree => {
157160
// commandTree.subtree('/!', { usage: usage.toplevel })
158161

159-
const shellFn = (_1, _2, fullArgv, _3, _4, execOptions, argv, options) => doShell(fullArgv, options, execOptions)
162+
const shellFn = (_1, _2, fullArgv, _3, command, execOptions, argv, options) => doShell(repl.split(command, false), options, execOptions)
160163
const shellCmd = commandTree.listen('/!', shellFn, { docs: 'Execute a UNIX shell command' })
161164

162-
commandTree.listen('/!/pwd', (_1, _2, fullArgv, _3, _4, execOptions, argv, options) => {
163-
return doShell(['!', 'pwd', ...argv.slice(1)], options, execOptions)
165+
commandTree.listen('/!/pwd', (_1, _2, fullArgv, _3, command, execOptions, argv, options) => {
166+
return doShell(['!', 'pwd', ...repl.split(command, false).slice(1)], options, execOptions)
164167
}, { docs: 'Print the current working directory' })
165168

166-
commandTree.listen('/!/lcd', (_1, _2, fullArgv, _3, _4, execOptions, argv, options) => {
167-
return doShell(['!', 'cd', ...argv.slice(1)], options, execOptions, Object.assign({}, execOptions, { nested: true }))
169+
commandTree.listen('/!/lcd', (_1, _2, fullArgv, _3, command, execOptions, argv, options) => {
170+
return doShell(['!', 'cd', ...repl.split(command, false).slice(1)], options, execOptions, Object.assign({}, execOptions, { nested: true }))
168171
.catch(message => { throw new errors.usage({ message, usage: usage.lcd }) })
169172
}, { usage: usage.lcd })
170173

171-
commandTree.listen('/!/lls', (_1, _2, fullArgv, { errors }, _4, execOptions, argv, options) => {
172-
return doShell(['!', 'ls', '-l', ...argv.slice(1)], options, Object.assign({}, execOptions, { nested: true }))
174+
commandTree.listen('/!/lls', (_1, _2, fullArgv, { errors }, command, execOptions, argv, options) => {
175+
return doShell(['!', 'ls', '-l', ...repl.split(command, false).slice(1)], options, Object.assign({}, execOptions, { nested: true }))
173176
.catch(message => { throw new errors.usage({ message, usage: usage.lls }) })
174177
}, { usage: usage.lls })
175178

176-
commandTree.listen('/!/lrm', (_1, _2, fullArgv, _3, _4, execOptions, argv, options) => {
177-
return doShell(['!', 'rm', ...argv.slice(1)], options, execOptions)
179+
commandTree.listen('/!/lrm', (_1, _2, fullArgv, _3, command, execOptions, argv, options) => {
180+
return doShell(['!', 'rm', ...repl.split(command, false).slice(1)], options, execOptions)
178181
.catch(message => { throw new errors.usage({ message, usage: usage.lrm }) })
179182
}, { usage: usage.lrm })
180183

app/plugins/ui/commands/tab-completion.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,22 @@ const listenForEscape = prompt => {
143143
return cleanup
144144
}
145145

146+
/** safeguard: only one tab completion temporary at a time, please */
147+
const cleaner = () => {
148+
const safeguard = document.querySelectorAll('.tab-completion-temporary')
149+
for (let idx = 0; idx < safeguard.length; idx++) {
150+
try {
151+
console.error('removing glitch')
152+
const old = safeguard[idx]
153+
if (old.parentNode) {
154+
old.parentNode.removeChid(safeguard)
155+
}
156+
} catch (err) {
157+
console.error('error removing glitch', err)
158+
}
159+
}
160+
}
161+
146162
/**
147163
* Make a container UI for tab completions
148164
*

tests/tests/passes/02/tab-completion.js

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('Tab completion', function() {
6262
if (!expected) {
6363
// then we expect non-visibility of the tab-completion popup
6464
// console.error('Expecting non-existence of popup')
65-
return app.client.waitForVisible(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary .clickable`, 5000, true)
65+
return app.client.waitForVisible(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary .clickable`, 20000, true)
6666
.then(() => {
6767
// great, the tab completion popup does not exist; early exit
6868
const err = new Error()
@@ -72,27 +72,27 @@ describe('Tab completion', function() {
7272
} else {
7373
const selector = `${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary .clickable`
7474
// console.error('Expecting existence of popup', selector)
75-
return app.client.waitForExist(selector, 5000)
75+
return app.client.waitForVisible(selector, 20000)
7676
}
7777
})
7878
.then(() => app.client.getText(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary .clickable`))
7979
.then(ui.expectArray(expected))
8080
// .then(() => { console.error('Got expected options') })
8181
.then(() => {
82-
if (click != undefined) {
82+
if (click !== undefined) {
8383
// click on a row
8484
const selector = `${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary .tab-completion-option[data-value="${expected[click]}"] .clickable`
85-
// console.error('clicking', click, selector)
86-
return app.client.waitForExist(selector, 5000)
85+
console.error('clicking', click, selector)
86+
return app.client.waitForVisible(selector, 20000)
8787
.then(() => app.client.click(selector))
8888
} else {
8989
// otherwise hit tab a number of times, to cycle to the desired entry
90-
// console.error('tabbing', nTabs)
90+
console.error('tabbing', nTabs)
9191
return doTimes(nTabs, () => app.client.keys('Tab'))
9292
.then(() => app.client.keys('Enter'))
9393
}
9494
})
95-
.then(() => app.client.waitForExist(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary`, 5000, true)) // wait for non-existence of the temporary
95+
.then(() => app.client.waitForVisible(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary`, 20000, true)) // wait for non-existence of the temporary
9696
.then(() => app.client.waitForValue(ui.selectors.PROMPT_N(count), full)))
9797
.then(() => cli.do('', app))
9898
.then(data => {
@@ -115,7 +115,7 @@ describe('Tab completion', function() {
115115
.then(() => app.client.getText(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary .clickable`))
116116
.then(ui.expectArray(expected))
117117
.then(() => app.client.keys('ffffff')) // type something random
118-
.then(() => app.client.waitForExist(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary`, 5000, true))) // wait for non-existence of the temporary
118+
.then(() => app.client.waitForVisible(`${ui.selectors.PROMPT_BLOCK_N(count)} .tab-completion-temporary`, 20000, true))) // wait for non-existence of the temporary
119119
.then(() => this.app.client.execute('repl.doCancel()')) // clear the line
120120
.catch(common.oops(this));
121121

@@ -164,12 +164,15 @@ describe('Tab completion', function() {
164164
.catch(common.oops(this)))
165165

166166
// expect b to autocomplete with only tab, since we only have one action starting with b
167-
it('should tab complete action bar', () => tabby(this.app, 'action get b', 'action get bar'))
168-
169-
it('should tab complete action foo with options', () => tabbyWithOptions(this.app, 'action get f',
170-
['foofoo/yum', 'foo2', 'foo'],
171-
'action get foo2',
172-
{ click: 1 })
167+
it('should tab complete action get bar', () => tabby(this.app, 'action get b', 'action get bar'))
168+
it('should tab complete action invoke bar', () => tabby(this.app, 'action invoke b', 'action invoke bar'))
169+
it('should tab complete invoke bar', () => tabby(this.app, 'invoke b', 'invoke bar'))
170+
it('should tab complete async bar', () => tabby(this.app, 'async b', 'async bar'))
171+
172+
it('should tab complete action foo2 with options', () => tabbyWithOptions(this.app, 'action get f',
173+
['foofoo/yum', 'foo2', 'foo'],
174+
'action get foo2',
175+
{ click: 1 })
173176
.then(sidecar.expectOpen)
174177
.then(sidecar.expectShowing('foo2'))
175178
.catch(common.oops(this)))

tests/tests/passes/04/zip.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ function main(args) {
206206
.then(sidecar.expectOpen)
207207
.then(sidecar.expectShowing(actionName18)))
208208
// invoke it
209-
it('should do an async of the action, using implicit context', () => cli.do(`async --param lines '[\"and now\", \"for something completely\", \"different\" ]'`, this.app)
209+
it('should do an async of the action, using implicit context', () => cli.do(`async --param lines '["and now", "for something completely", "different" ]'`, this.app)
210210
.then(cli.expectJustOK))
211211
// call await
212212
it('should await successful completion of the activation', () => cli.do(`$ await`, this.app)

0 commit comments

Comments
 (0)