Notebook StdIn support to fix #5231 (#5232)

Fixes #5231 
- Add stdin handling. Has to be at UI level so add plumb through handling
- Add unit tests
- Add new StdIn component.

Testing:
Unit Tests and manual testing of following:
- Prompt for password using `getpass` in python.
   - Password prompt is hidden since "password" is true.
   - Hit enter, it completes
- prompt, stop cell running, StdIn disappears
- prompt, hit escape, stdIn disappears and stdIn request is handled.

Issues: focus isn't always set to the input even though we call focus.
Will investigate this further.
This commit is contained in:
Kevin Cunnane
2019-04-30 14:57:27 -07:00
committed by GitHub
parent b21125ff2d
commit 64416e05c1
11 changed files with 285 additions and 5 deletions

View File

@@ -16,6 +16,7 @@ import { ModelFactory } from 'sql/workbench/parts/notebook/models/modelFactory';
import { NotebookModelStub } from '../common';
import { EmptyFuture } from 'sql/workbench/services/notebook/common/sessionManager';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { Deferred } from 'sql/base/common/promise';
suite('Cell Model', function (): void {
let factory = new ModelFactory();
@@ -185,6 +186,19 @@ suite('Cell Model', function (): void {
suite('Model Future handling', function (): void {
let future: TypeMoq.Mock<EmptyFuture>;
let cell: ICellModel;
const stdInDefaultMessage: nb.IStdinMessage = {
channel: 'stdin',
type: 'stdin',
parent_header: undefined,
metadata: undefined,
header: <nb.IHeader>{
msg_type: 'stream'
},
content: {
prompt: 'Prompt',
password: false
}
};
setup(() => {
future = TypeMoq.Mock.ofType(EmptyFuture);
cell = factory.createCell({
@@ -239,8 +253,72 @@ suite('Cell Model', function (): void {
message.header.msg_type = 'display_data';
onIopub.handle(message);
should(outputs[1].output_type).equal('display_data');
});
// ... TODO: And when I sent a reply I expect it to be processed.
test('stdin should return void if no handler registered', async () => {
// Given stdIn does not have a request handler setup
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
// When I set it on the cell
cell.setFuture(future.object);
// Then I expect stdIn to have been hooked up
should(onStdIn).not.be.undefined();
// ... And when I send a stdIn request message
let result = onStdIn.handle(stdInDefaultMessage);
// Then I expect the promise to resolve
await result;
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.never());
});
test('stdin should wait on handler if handler registered', async () => {
// Given stdIn has a handler set up
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
let deferred = new Deferred<void>();
let stdInMessage: nb.IStdinMessage = undefined;
cell.setStdInHandler({
handle: (msg: nb.IStdinMessage) => {
stdInMessage = msg;
return deferred.promise;
}
});
// When I send a stdIn request message
cell.setFuture(future.object);
let result = onStdIn.handle(stdInDefaultMessage);
deferred.resolve();
// Then I expect promise to resolve since it should wait on upstream handling
await result;
// And I expect message to have been passed upstream and no message sent from the cell
should(stdInMessage).not.be.undefined();
should(stdInMessage.content.prompt).equal(stdInDefaultMessage.content.prompt);
should(stdInMessage.content.password).equal(stdInDefaultMessage.content.password);
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.never());
});
test('stdin should send default response if there is upstream error', async () => {
// Given stdIn has a handler set up
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
let deferred = new Deferred<void>();
let stdInMessage: nb.IStdinMessage = undefined;
cell.setStdInHandler({
handle: (msg: nb.IStdinMessage) => {
stdInMessage = msg;
return deferred.promise;
}
});
// When I send a stdIn request message
cell.setFuture(future.object);
let result = onStdIn.handle(stdInDefaultMessage);
deferred.reject('Something went wrong');
// Then I expect promise to resolve since it should wait on upstream handling
await result;
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
test('should delete transient tag while handling incoming messages', async () => {