mock.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import 'jest';
  4. import { ISessionContext, SessionContext } from '@jupyterlab/apputils';
  5. import { Context, TextModelFactory } from '@jupyterlab/docregistry';
  6. import {
  7. Kernel,
  8. KernelMessage,
  9. KernelSpec,
  10. Session,
  11. ServiceManager,
  12. Contents,
  13. ServerConnection,
  14. ContentsManager
  15. } from '@jupyterlab/services';
  16. import { ArrayIterator } from '@lumino/algorithm';
  17. import { AttachedProperty } from '@lumino/properties';
  18. import { UUID } from '@lumino/coreutils';
  19. import { Signal } from '@lumino/signaling';
  20. import { PathExt } from '@jupyterlab/coreutils';
  21. export const KERNELSPECS: KernelSpec.ISpecModel[] = [
  22. {
  23. argv: [
  24. '/Users/someuser/miniconda3/envs/jupyterlab/bin/python',
  25. '-m',
  26. 'ipykernel_launcher',
  27. '-f',
  28. '{connection_file}'
  29. ],
  30. display_name: 'Python 3',
  31. language: 'python',
  32. metadata: {},
  33. name: 'python3',
  34. resources: {}
  35. },
  36. {
  37. argv: [
  38. '/Users/someuser/miniconda3/envs/jupyterlab/bin/python',
  39. '-m',
  40. 'ipykernel_launcher',
  41. '-f',
  42. '{connection_file}'
  43. ],
  44. display_name: 'R',
  45. language: 'python',
  46. metadata: {},
  47. name: 'irkernel',
  48. resources: {}
  49. }
  50. ];
  51. export const KERNEL_MODELS: Kernel.IModel[] = [
  52. {
  53. name: 'python3',
  54. id: UUID.uuid4()
  55. },
  56. {
  57. name: 'r',
  58. id: UUID.uuid4()
  59. },
  60. {
  61. name: 'python3',
  62. id: UUID.uuid4()
  63. }
  64. ];
  65. // Notebook Paths for certain kernel name
  66. export const NOTEBOOK_PATHS: { [kernelName: string]: string[] } = {
  67. python3: ['Untitled.ipynb', 'Untitled1.ipynb', 'Untitled2.ipynb'],
  68. r: ['Visualization.ipynb', 'Analysis.ipynb', 'Conclusion.ipynb']
  69. };
  70. /**
  71. * Forceably change the status of a session context.
  72. * An iopub message is emitted for the change.
  73. *
  74. * @param sessionContext The session context of interest.
  75. * @param newStatus The new kernel status.
  76. */
  77. export function updateKernelStatus(
  78. sessionContext: ISessionContext,
  79. newStatus: KernelMessage.Status
  80. ) {
  81. const kernel = sessionContext.session!.kernel!;
  82. (kernel as any).status = newStatus;
  83. (sessionContext.statusChanged as any).emit(newStatus);
  84. const msg = KernelMessage.createMessage({
  85. session: kernel.clientId,
  86. channel: 'iopub',
  87. msgType: 'status',
  88. content: { execution_state: newStatus }
  89. });
  90. emitIopubMessage(sessionContext, msg);
  91. }
  92. /**
  93. * Emit an iopub message on a session context.
  94. *
  95. * @param sessionContext The session context
  96. * @param msg Message created with `KernelMessage.createMessage`
  97. */
  98. export function emitIopubMessage(
  99. context: ISessionContext,
  100. msg: KernelMessage.IIOPubMessage
  101. ): void {
  102. const kernel = context!.session!.kernel!;
  103. const msgId = Private.lastMessageProperty.get(kernel);
  104. (msg.parent_header as any).session = kernel.clientId;
  105. (msg.parent_header as any).msg_id = msgId;
  106. (kernel.iopubMessage as any).emit(msg);
  107. }
  108. /**
  109. * Create a session context given a partial session model.
  110. *
  111. * @param model The session model to use.
  112. */
  113. export function createSimpleSessionContext(
  114. model: Private.RecursivePartial<Session.IModel> = {}
  115. ): ISessionContext {
  116. const kernel = new KernelMock({ model: model?.kernel || {} });
  117. const session = new SessionConnectionMock({ model }, kernel);
  118. return new SessionContextMock({}, session);
  119. }
  120. /**
  121. * Clone a kernel connection.
  122. */
  123. export function cloneKernel(
  124. kernel: Kernel.IKernelConnection
  125. ): Kernel.IKernelConnection {
  126. return (kernel as any).clone();
  127. }
  128. /**
  129. * A mock kernel object.
  130. *
  131. * @param model The model of the kernel
  132. */
  133. export const KernelMock = jest.fn<
  134. Kernel.IKernelConnection,
  135. [Private.RecursivePartial<Kernel.IKernelConnection.IOptions>]
  136. >(options => {
  137. const model = options.model || {};
  138. if (!model.id) {
  139. (model! as any).id = 'foo';
  140. }
  141. if (!model.name) {
  142. (model! as any).name = KERNEL_MODELS[0].name;
  143. }
  144. options = {
  145. clientId: UUID.uuid4(),
  146. username: UUID.uuid4(),
  147. ...options,
  148. model
  149. };
  150. let executionCount = 0;
  151. const spec = Private.kernelSpecForKernelName(model!.name!)!;
  152. const thisObject: Kernel.IKernelConnection = {
  153. ...jest.requireActual('@jupyterlab/services'),
  154. ...options,
  155. ...model,
  156. status: 'idle',
  157. spec: () => {
  158. return Promise.resolve(spec);
  159. },
  160. dispose: jest.fn(),
  161. clone: jest.fn(() => {
  162. const newKernel = Private.cloneKernel(options);
  163. newKernel.iopubMessage.connect((_, args) => {
  164. iopubMessageSignal.emit(args);
  165. });
  166. newKernel.statusChanged.connect((_, args) => {
  167. (thisObject as any).status = args;
  168. statusChangedSignal.emit(args);
  169. });
  170. return newKernel;
  171. }),
  172. info: jest.fn(Promise.resolve),
  173. shutdown: jest.fn(Promise.resolve),
  174. requestHistory: jest.fn(() => {
  175. const historyReply = KernelMessage.createMessage({
  176. channel: 'shell',
  177. msgType: 'history_reply',
  178. session: options.clientId!,
  179. username: options.username!,
  180. content: {
  181. history: [],
  182. status: 'ok'
  183. }
  184. });
  185. return Promise.resolve(historyReply);
  186. }),
  187. requestExecute: jest.fn(options => {
  188. const msgId = UUID.uuid4();
  189. executionCount++;
  190. Private.lastMessageProperty.set(thisObject, msgId);
  191. const msg = KernelMessage.createMessage({
  192. channel: 'iopub',
  193. msgType: 'execute_input',
  194. session: thisObject.clientId,
  195. username: thisObject.username,
  196. msgId,
  197. content: {
  198. code: options.code,
  199. execution_count: executionCount
  200. }
  201. });
  202. iopubMessageSignal.emit(msg);
  203. return new MockShellFuture();
  204. })
  205. };
  206. // Add signals.
  207. const iopubMessageSignal = new Signal<
  208. Kernel.IKernelConnection,
  209. KernelMessage.IIOPubMessage
  210. >(thisObject);
  211. const statusChangedSignal = new Signal<
  212. Kernel.IKernelConnection,
  213. Kernel.Status
  214. >(thisObject);
  215. (thisObject as any).statusChanged = statusChangedSignal;
  216. (thisObject as any).iopubMessage = iopubMessageSignal;
  217. return thisObject;
  218. });
  219. /**
  220. * A mock session connection.
  221. *
  222. * @param options Addition session options to use
  223. * @param model A session model to use
  224. */
  225. export const SessionConnectionMock = jest.fn<
  226. Session.ISessionConnection,
  227. [
  228. Private.RecursivePartial<Session.ISessionConnection.IOptions>,
  229. Kernel.IKernelConnection | null
  230. ]
  231. >((options, kernel) => {
  232. const name = kernel?.name || options.model?.name || KERNEL_MODELS[0].name;
  233. kernel = kernel || new KernelMock({ model: { name } });
  234. const model = {
  235. path: 'foo',
  236. type: 'notebook',
  237. name: 'foo',
  238. ...options.model,
  239. kernel: kernel!.model
  240. };
  241. const thisObject: Session.ISessionConnection = {
  242. ...jest.requireActual('@jupyterlab/services'),
  243. id: UUID.uuid4(),
  244. ...options,
  245. model,
  246. ...model,
  247. kernel,
  248. dispose: jest.fn(),
  249. changeKernel: jest.fn(partialModel => {
  250. return Private.changeKernel(kernel!, partialModel!);
  251. }),
  252. selectKernel: jest.fn(),
  253. shutdown: jest.fn(() => Promise.resolve(void 0))
  254. };
  255. const disposedSignal = new Signal<Session.ISessionConnection, undefined>(
  256. thisObject
  257. );
  258. const propertyChangedSignal = new Signal<
  259. Session.ISessionConnection,
  260. 'path' | 'name' | 'type'
  261. >(thisObject);
  262. const statusChangedSignal = new Signal<
  263. Session.ISessionConnection,
  264. Kernel.Status
  265. >(thisObject);
  266. const connectionStatusChangedSignal = new Signal<
  267. Session.ISessionConnection,
  268. Kernel.ConnectionStatus
  269. >(thisObject);
  270. const kernelChangedSignal = new Signal<
  271. Session.ISessionConnection,
  272. Session.ISessionConnection.IKernelChangedArgs
  273. >(thisObject);
  274. const iopubMessageSignal = new Signal<
  275. Session.ISessionConnection,
  276. KernelMessage.IIOPubMessage
  277. >(thisObject);
  278. const unhandledMessageSignal = new Signal<
  279. Session.ISessionConnection,
  280. KernelMessage.IMessage
  281. >(thisObject);
  282. kernel!.iopubMessage.connect((_, args) => {
  283. iopubMessageSignal.emit(args);
  284. }, thisObject);
  285. kernel!.statusChanged.connect((_, args) => {
  286. statusChangedSignal.emit(args);
  287. }, thisObject);
  288. (thisObject as any).disposed = disposedSignal;
  289. (thisObject as any).connectionStatusChanged = connectionStatusChangedSignal;
  290. (thisObject as any).propertyChanged = propertyChangedSignal;
  291. (thisObject as any).statusChanged = statusChangedSignal;
  292. (thisObject as any).kernelChanged = kernelChangedSignal;
  293. (thisObject as any).iopubMessage = iopubMessageSignal;
  294. (thisObject as any).unhandledMessage = unhandledMessageSignal;
  295. return thisObject;
  296. });
  297. /**
  298. * A mock session context.
  299. *
  300. * @param session The session connection object to use
  301. */
  302. export const SessionContextMock = jest.fn<
  303. ISessionContext,
  304. [Partial<SessionContext.IOptions>, Session.ISessionConnection | null]
  305. >((options, connection) => {
  306. const session =
  307. connection ||
  308. new SessionConnectionMock(
  309. {
  310. model: {
  311. path: options.path || '',
  312. type: options.type || '',
  313. name: options.name || ''
  314. }
  315. },
  316. null
  317. );
  318. const thisObject: ISessionContext = {
  319. ...jest.requireActual('@jupyterlab/apputils'),
  320. ...options,
  321. path: session.path,
  322. type: session.type,
  323. name: session.name,
  324. kernel: session.kernel,
  325. session,
  326. dispose: jest.fn(),
  327. initialize: jest.fn(() => Promise.resolve(void 0)),
  328. ready: Promise.resolve(void 0),
  329. changeKernel: jest.fn(partialModel => {
  330. return Private.changeKernel(
  331. session.kernel || Private.RUNNING_KERNELS[0],
  332. partialModel!
  333. );
  334. }),
  335. shutdown: jest.fn(() => Promise.resolve(void 0))
  336. };
  337. const disposedSignal = new Signal<ISessionContext, undefined>(thisObject);
  338. const propertyChangedSignal = new Signal<
  339. ISessionContext,
  340. 'path' | 'name' | 'type'
  341. >(thisObject);
  342. const statusChangedSignal = new Signal<ISessionContext, Kernel.Status>(
  343. thisObject
  344. );
  345. const kernelChangedSignal = new Signal<
  346. ISessionContext,
  347. Session.ISessionConnection.IKernelChangedArgs
  348. >(thisObject);
  349. const iopubMessageSignal = new Signal<
  350. ISessionContext,
  351. KernelMessage.IIOPubMessage
  352. >(thisObject);
  353. session!.statusChanged.connect((_, args) => {
  354. statusChangedSignal.emit(args);
  355. }, thisObject);
  356. session!.iopubMessage.connect((_, args) => {
  357. iopubMessageSignal.emit(args);
  358. });
  359. session!.kernelChanged.connect((_, args) => {
  360. kernelChangedSignal.emit(args);
  361. });
  362. (thisObject as any).statusChanged = statusChangedSignal;
  363. (thisObject as any).kernelChanged = kernelChangedSignal;
  364. (thisObject as any).iopubMessage = iopubMessageSignal;
  365. (thisObject as any).propertyChanged = propertyChangedSignal;
  366. (thisObject as any).disposed = disposedSignal;
  367. (thisObject as any).session = session;
  368. return thisObject;
  369. });
  370. /**
  371. * A mock contents manager.
  372. */
  373. export const ContentsManagerMock = jest.fn<Contents.IManager, []>(() => {
  374. const files = new Map<string, Contents.IModel>();
  375. const dummy = new ContentsManager();
  376. const checkpoints: { [key: string]: Contents.ICheckpointModel } = {};
  377. const baseModel = Private.createFile({ type: 'directory' });
  378. files.set('', { ...baseModel, path: '', name: '' });
  379. const thisObject: Contents.IManager = {
  380. ...jest.requireActual('@jupyterlab/services'),
  381. ready: Promise.resolve(void 0),
  382. newUntitled: jest.fn(options => {
  383. const model = Private.createFile(options || {});
  384. files.set(model.path, model);
  385. fileChangedSignal.emit({
  386. type: 'new',
  387. oldValue: null,
  388. newValue: model
  389. });
  390. return Promise.resolve(model);
  391. }),
  392. createCheckpoint: jest.fn(path => {
  393. const lastModified = new Date().toISOString();
  394. checkpoints[path] = { id: UUID.uuid4(), last_modified: lastModified };
  395. return Promise.resolve();
  396. }),
  397. listCheckpoints: jest.fn(path => {
  398. if (checkpoints[path]) {
  399. return Promise.resolve([checkpoints[path]]);
  400. }
  401. return Promise.resolve([]);
  402. }),
  403. getModelDBFactory: jest.fn(() => {
  404. return null;
  405. }),
  406. normalize: jest.fn(path => {
  407. return dummy.normalize(path);
  408. }),
  409. localPath: jest.fn(path => {
  410. return dummy.localPath(path);
  411. }),
  412. get: jest.fn((path, options) => {
  413. path = Private.fixSlash(path);
  414. if (!files.has(path)) {
  415. return Private.makeResponseError(404);
  416. }
  417. const model = files.get(path)!;
  418. if (model.type === 'directory') {
  419. if (options?.content !== false) {
  420. const content: Contents.IModel[] = [];
  421. files.forEach(fileModel => {
  422. if (PathExt.dirname(fileModel.path) == model.path) {
  423. content.push(fileModel);
  424. }
  425. });
  426. return Promise.resolve({ ...model, content });
  427. }
  428. return Promise.resolve(model);
  429. }
  430. if (options?.content != false) {
  431. return Promise.resolve(model);
  432. }
  433. return Promise.resolve({ ...model, content: '' });
  434. }),
  435. driveName: jest.fn(path => {
  436. return dummy.driveName(path);
  437. }),
  438. rename: jest.fn((oldPath, newPath) => {
  439. oldPath = Private.fixSlash(oldPath);
  440. newPath = Private.fixSlash(newPath);
  441. if (!files.has(oldPath)) {
  442. return Private.makeResponseError(404);
  443. }
  444. const oldValue = files.get(oldPath)!;
  445. files.delete(oldPath);
  446. const name = PathExt.basename(newPath);
  447. const newValue = { ...oldValue, name, path: newPath };
  448. files.set(newPath, newValue);
  449. fileChangedSignal.emit({
  450. type: 'rename',
  451. oldValue,
  452. newValue
  453. });
  454. return Promise.resolve(newValue);
  455. }),
  456. delete: jest.fn(path => {
  457. path = Private.fixSlash(path);
  458. if (!files.has(path)) {
  459. return Private.makeResponseError(404);
  460. }
  461. const oldValue = files.get(path)!;
  462. files.delete(path);
  463. fileChangedSignal.emit({
  464. type: 'delete',
  465. oldValue,
  466. newValue: null
  467. });
  468. return Promise.resolve(void 0);
  469. }),
  470. save: jest.fn((path, options) => {
  471. path = Private.fixSlash(path);
  472. const timeStamp = new Date().toISOString();
  473. if (files.has(path)) {
  474. files.set(path, {
  475. ...files.get(path)!,
  476. ...options,
  477. last_modified: timeStamp
  478. });
  479. } else {
  480. files.set(path, {
  481. path,
  482. name: PathExt.basename(path),
  483. content: '',
  484. writable: true,
  485. created: timeStamp,
  486. type: 'file',
  487. format: 'text',
  488. mimetype: 'plain/text',
  489. ...options,
  490. last_modified: timeStamp
  491. });
  492. }
  493. fileChangedSignal.emit({
  494. type: 'save',
  495. oldValue: null,
  496. newValue: files.get(path)!
  497. });
  498. return Promise.resolve(files.get(path)!);
  499. }),
  500. getDownloadUrl: jest.fn(path => {
  501. return dummy.getDownloadUrl(path);
  502. }),
  503. addDrive: jest.fn(drive => {
  504. dummy.addDrive(drive);
  505. }),
  506. dispose: jest.fn()
  507. };
  508. const fileChangedSignal = new Signal<
  509. Contents.IManager,
  510. Contents.IChangedArgs
  511. >(thisObject);
  512. (thisObject as any).fileChanged = fileChangedSignal;
  513. return thisObject;
  514. });
  515. /**
  516. * A mock sessions manager.
  517. */
  518. export const SessionManagerMock = jest.fn<Session.IManager, []>(() => {
  519. let sessions: Session.IModel[] = [];
  520. const thisObject: Session.IManager = {
  521. ...jest.requireActual('@jupyterlab/services'),
  522. ready: Promise.resolve(void 0),
  523. startNew: jest.fn(options => {
  524. const session = new SessionConnectionMock({ model: options }, null);
  525. sessions.push(session.model);
  526. runningChangedSignal.emit(sessions);
  527. return session;
  528. }),
  529. connectTo: jest.fn(options => {
  530. return new SessionConnectionMock(options, null);
  531. }),
  532. stopIfNeeded: jest.fn(path => {
  533. const length = sessions.length;
  534. sessions = sessions.filter(model => model.path !== path);
  535. if (sessions.length !== length) {
  536. runningChangedSignal.emit(sessions);
  537. }
  538. return Promise.resolve(void 0);
  539. }),
  540. refreshRunning: jest.fn(() => Promise.resolve(void 0)),
  541. running: jest.fn(() => new ArrayIterator(sessions))
  542. };
  543. const runningChangedSignal = new Signal<Session.IManager, Session.IModel[]>(
  544. thisObject
  545. );
  546. (thisObject as any).runningChanged = runningChangedSignal;
  547. return thisObject;
  548. });
  549. /**
  550. * A mock kernel specs manager
  551. */
  552. export const KernelSpecManagerMock = jest.fn<KernelSpec.IManager, []>(() => {
  553. const thisObject: KernelSpec.IManager = {
  554. ...jest.requireActual('@jupyterlab/services'),
  555. specs: { default: KERNELSPECS[0].name, kernelspecs: KERNELSPECS },
  556. refreshSpecs: jest.fn(() => Promise.resolve(void 0))
  557. };
  558. return thisObject;
  559. });
  560. /**
  561. * A mock service manager.
  562. */
  563. export const ServiceManagerMock = jest.fn<ServiceManager.IManager, []>(() => {
  564. const thisObject: ServiceManager.IManager = {
  565. ...jest.requireActual('@jupyterlab/services'),
  566. ready: Promise.resolve(void 0),
  567. contents: new ContentsManagerMock(),
  568. sessions: new SessionManagerMock(),
  569. kernelspecs: new KernelSpecManagerMock(),
  570. dispose: jest.fn()
  571. };
  572. return thisObject;
  573. });
  574. /**
  575. * A mock kernel shell future.
  576. */
  577. export const MockShellFuture = jest.fn<Kernel.IShellFuture, []>(() => {
  578. const thisObject: Kernel.IShellFuture = {
  579. ...jest.requireActual('@jupyterlab/services'),
  580. done: Promise.resolve(void 0)
  581. };
  582. return thisObject;
  583. });
  584. /**
  585. * Create a context for a file.
  586. */
  587. export function createFileContext(startKernel = false): Context {
  588. const path = UUID.uuid4() + '.txt';
  589. const manager = new ServiceManagerMock();
  590. const factory = new TextModelFactory();
  591. return new Context({
  592. manager,
  593. factory,
  594. path,
  595. kernelPreference: {
  596. shouldStart: startKernel,
  597. canStart: startKernel,
  598. autoStartDefault: startKernel
  599. }
  600. });
  601. }
  602. /**
  603. * A namespace for module private data.
  604. */
  605. namespace Private {
  606. export function flattenArray<T>(arr: T[][]): T[] {
  607. const result: T[] = [];
  608. arr.forEach(innerArr => {
  609. innerArr.forEach(elem => {
  610. result.push(elem);
  611. });
  612. });
  613. return result;
  614. }
  615. export type RecursivePartial<T> = {
  616. [P in keyof T]?: RecursivePartial<T[P]>;
  617. };
  618. export function createFile(
  619. options?: Contents.ICreateOptions
  620. ): Contents.IModel {
  621. options = options || {};
  622. let name = UUID.uuid4();
  623. switch (options.type) {
  624. case 'directory':
  625. name = `Untitled Folder_${name}`;
  626. break;
  627. case 'notebook':
  628. name = `Untitled_${name}.ipynb`;
  629. break;
  630. default:
  631. name = `untitled_${name}${options.ext || '.txt'}`;
  632. }
  633. const path = PathExt.join(options.path || '', name);
  634. let content = '';
  635. if (options.type === 'notebook') {
  636. content = JSON.stringify({});
  637. }
  638. const timeStamp = new Date().toISOString();
  639. return {
  640. path,
  641. content,
  642. name,
  643. last_modified: timeStamp,
  644. writable: true,
  645. created: timeStamp,
  646. type: options.type || 'file',
  647. format: 'text',
  648. mimetype: 'plain/text'
  649. };
  650. }
  651. export function fixSlash(path: string): string {
  652. if (path.endsWith('/')) {
  653. path = path.slice(0, path.length - 1);
  654. }
  655. return path;
  656. }
  657. export function makeResponseError(
  658. status: number
  659. ): Promise<ServerConnection.ResponseError> {
  660. const resp = new Response(void 0, { status });
  661. return Promise.reject(new ServerConnection.ResponseError(resp));
  662. }
  663. export function cloneKernel(
  664. options: RecursivePartial<Kernel.IKernelConnection.IOptions>
  665. ): Kernel.IKernelConnection {
  666. return new KernelMock(options);
  667. }
  668. // Get the kernel spec for kernel name
  669. export function kernelSpecForKernelName(name: string) {
  670. return KERNELSPECS.find(val => {
  671. return val.name === name;
  672. });
  673. }
  674. export function changeKernel(
  675. kernel: Kernel.IKernelConnection,
  676. partialModel: Partial<Kernel.IModel>
  677. ): Promise<Kernel.IModel> {
  678. if (partialModel.id) {
  679. const kernelIdx = KERNEL_MODELS.findIndex(model => {
  680. return model.id === partialModel.id;
  681. });
  682. if (kernelIdx !== -1) {
  683. (kernel.model as any) = RUNNING_KERNELS[kernelIdx].model;
  684. (kernel.id as any) = partialModel.id;
  685. return Promise.resolve(RUNNING_KERNELS[kernelIdx]);
  686. } else {
  687. throw new Error(
  688. `Unable to change kernel to one with id: ${partialModel.id}`
  689. );
  690. }
  691. } else if (partialModel.name) {
  692. const kernelIdx = KERNEL_MODELS.findIndex(model => {
  693. return model.name === partialModel.name;
  694. });
  695. if (kernelIdx !== -1) {
  696. (kernel.model as any) = RUNNING_KERNELS[kernelIdx].model;
  697. (kernel.id as any) = partialModel.id;
  698. return Promise.resolve(RUNNING_KERNELS[kernelIdx]);
  699. } else {
  700. throw new Error(
  701. `Unable to change kernel to one with name: ${partialModel.name}`
  702. );
  703. }
  704. } else {
  705. throw new Error(`Unable to change kernel`);
  706. }
  707. }
  708. // This list of running kernels simply mirrors the KERNEL_MODELS and KERNELSPECS lists
  709. export const RUNNING_KERNELS: Kernel.IKernelConnection[] = KERNEL_MODELS.map(
  710. (model, _) => {
  711. return new KernelMock({ model });
  712. }
  713. );
  714. export const lastMessageProperty = new AttachedProperty<
  715. Kernel.IKernelConnection,
  716. string
  717. >({
  718. name: 'lastMessageId',
  719. create: () => ''
  720. });
  721. }