foreign.spec.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import expect = require('expect.js');
  4. import {
  5. KernelMessage, utils, Session
  6. } from '@jupyterlab/services';
  7. import {
  8. Signal
  9. } from '@phosphor/signaling';
  10. import {
  11. Panel
  12. } from '@phosphor/widgets';
  13. import {
  14. IClientSession
  15. } from '@jupyterlab/apputils';
  16. import {
  17. ForeignHandler
  18. } from '@jupyterlab/console';
  19. import {
  20. CodeCellModel, CodeCellWidget
  21. } from '@jupyterlab/cells';
  22. import {
  23. createCodeCellFactory
  24. } from '../notebook/utils';
  25. import {
  26. createClientSession, defaultRenderMime
  27. } from '../utils';
  28. class TestParent extends Panel implements ForeignHandler.IReceiver {
  29. addCell(cell: CodeCellWidget): void {
  30. this.addWidget(cell);
  31. }
  32. }
  33. class TestHandler extends ForeignHandler {
  34. injected = new Signal<this, void>(this);
  35. received = new Signal<this, void>(this);
  36. rejected = new Signal<this, void>(this);
  37. methods: string[] = [];
  38. protected onIOPubMessage(sender: IClientSession, msg: KernelMessage.IIOPubMessage): boolean {
  39. let injected = super.onIOPubMessage(sender, msg);
  40. this.received.emit(void 0);
  41. if (injected) {
  42. this.injected.emit(void 0);
  43. } else {
  44. // If the message was not injected but otherwise would have been, emit
  45. // a rejected signal. This should only happen if `enabled` is `false`.
  46. let session = (msg.parent_header as KernelMessage.IHeader).session;
  47. let msgType = msg.header.msg_type;
  48. if (session !== this.session.kernel.clientId && relevantTypes.has(msgType)) {
  49. this.rejected.emit(void 0);
  50. }
  51. }
  52. return injected;
  53. }
  54. }
  55. const rendermime = defaultRenderMime();
  56. function cellFactory(): CodeCellWidget {
  57. let contentFactory = createCodeCellFactory();
  58. let model = new CodeCellModel({});
  59. let cell = new CodeCellWidget({ model, rendermime, contentFactory });
  60. return cell;
  61. };
  62. const relevantTypes = [
  63. 'execute_input',
  64. 'execute_result',
  65. 'display_data',
  66. 'stream',
  67. 'error',
  68. 'clear_output'
  69. ].reduce((acc, val) => {
  70. acc.add(val);
  71. return acc;
  72. }, new Set<string>());
  73. describe('@jupyterlab/console', () => {
  74. describe('ForeignHandler', () => {
  75. let local: Session.ISession;
  76. let foreign: Session.ISession;
  77. let handler: TestHandler;
  78. let session: IClientSession;
  79. before(() => {
  80. let path = utils.uuid();
  81. let sessions = [Session.startNew({ path }), Session.startNew({ path })];
  82. return Promise.all(sessions).then(([one, two]) => {
  83. local = one;
  84. foreign = two;
  85. }).then(() => {
  86. return createClientSession({ path: local.path });
  87. }).then(s => {
  88. session = s;
  89. return s.initialize();
  90. });
  91. });
  92. beforeEach(() => {
  93. let parent = new TestParent();
  94. handler = new TestHandler({ session, parent, cellFactory });
  95. });
  96. afterEach(() => {
  97. handler.dispose();
  98. });
  99. after(() => {
  100. local.dispose();
  101. foreign.dispose();
  102. return session.shutdown().then(() => {
  103. session.dispose();
  104. });
  105. });
  106. describe('#constructor()', () => {
  107. it('should create a new foreign handler', () => {
  108. expect(handler).to.be.a(ForeignHandler);
  109. });
  110. });
  111. describe('#enabled', () => {
  112. it('should default to `true`', () => {
  113. expect(handler.enabled).to.be(true);
  114. });
  115. it('should allow foreign cells to be injected if `true`', done => {
  116. let code = 'print("#enabled:true")';
  117. handler.injected.connect(() => { done(); });
  118. foreign.kernel.requestExecute({ code, stop_on_error: true });
  119. });
  120. it('should reject foreign cells if `false`', done => {
  121. let code = 'print("#enabled:false")';
  122. handler.enabled = false;
  123. handler.rejected.connect(() => { done(); });
  124. foreign.kernel.requestExecute({ code, stop_on_error: true });
  125. });
  126. });
  127. describe('#isDisposed', () => {
  128. it('should indicate whether the handler is disposed', () => {
  129. expect(handler.isDisposed).to.be(false);
  130. handler.dispose();
  131. expect(handler.isDisposed).to.be(true);
  132. });
  133. });
  134. describe('#session', () => {
  135. it('should be a client session object', () => {
  136. expect(handler.session.path).to.ok();
  137. });
  138. });
  139. describe('#parent', () => {
  140. it('should be set upon instantiation', () => {
  141. let parent = new TestParent();
  142. handler = new TestHandler({
  143. session: handler.session, parent, cellFactory
  144. });
  145. expect(handler.parent).to.be(parent);
  146. });
  147. });
  148. describe('#dispose()', () => {
  149. it('should dispose the resources held by the handler', () => {
  150. expect(handler.isDisposed).to.be(false);
  151. handler.dispose();
  152. expect(handler.isDisposed).to.be(true);
  153. });
  154. it('should be safe to call multiple times', () => {
  155. expect(handler.isDisposed).to.be(false);
  156. handler.dispose();
  157. handler.dispose();
  158. expect(handler.isDisposed).to.be(true);
  159. });
  160. });
  161. describe('#onIOPubMessage()', () => {
  162. it('should be called when messages come through', done => {
  163. let code = 'print("onIOPubMessage:disabled")';
  164. handler.enabled = false;
  165. handler.received.connect(() => { done(); });
  166. foreign.kernel.requestExecute({ code, stop_on_error: true });
  167. });
  168. it('should inject relevant cells into the parent', done => {
  169. let code = 'print("#onIOPubMessage:enabled")';
  170. let parent = handler.parent as TestParent;
  171. expect(parent.widgets.length).to.be(0);
  172. handler.injected.connect(() => {
  173. expect(parent.widgets.length).to.be.greaterThan(0);
  174. done();
  175. });
  176. foreign.kernel.requestExecute({ code, stop_on_error: true });
  177. });
  178. it('should not reject relevant iopub messages', done => {
  179. let code = 'print("#onIOPubMessage:relevant")';
  180. let called = 0;
  181. handler.rejected.connect(() => {
  182. done(new Error('rejected relevant iopub message'));
  183. });
  184. handler.injected.connect(() => {
  185. if (++called === 2) {
  186. done();
  187. }
  188. });
  189. foreign.kernel.requestExecute({ code, stop_on_error: true });
  190. });
  191. });
  192. });
  193. });