@jupyterlab/docregistry
Version:
JupyterLab - Document Registry
288 lines (255 loc) • 7.69 kB
text/typescript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// We explicitly reference the jest typings since the jest.d.ts file shipped
// with jest 26 masks the @types/jest typings
/// <reference types="jest" />
import { ISessionContext, SessionContext } from '@jupyterlab/apputils';
import {
Kernel,
KernelMessage,
ServiceManager,
Session
} from '@jupyterlab/services';
import {
changeKernel,
KERNEL_MODELS,
KernelMock,
ServiceManagerMock,
SessionConnectionMock
} from '@jupyterlab/services/lib/testutils';
import { UUID } from '@lumino/coreutils';
import { AttachedProperty } from '@lumino/properties';
import { ISignal, Signal } from '@lumino/signaling';
import { Context } from './context';
import { TextModelFactory } from './default';
import { DocumentRegistry, IDocumentWidget } from './registry';
/**
* Create a context for a file.
*/
export function createFileContext(
path: string = UUID.uuid4() + '.txt',
manager: ServiceManager.IManager = Private.getManager()
): Context<DocumentRegistry.IModel> {
const factory = Private.textFactory;
return new Context({ manager, factory, path });
}
export async function createFileContextWithKernel(
path: string = UUID.uuid4() + '.txt',
manager: ServiceManager.IManager = Private.getManager()
): Promise<Context> {
const factory = Private.textFactory;
const specsManager = manager.kernelspecs;
await specsManager.ready;
return new Context({
manager,
factory,
path,
kernelPreference: {
shouldStart: true,
canStart: true,
name: specsManager.specs?.default
}
});
}
export async function createFileContextWithMockedServices(
startKernel = false,
manager?: ServiceManager.IManager
): Promise<Context> {
const path = UUID.uuid4() + '.txt';
manager = manager || new ServiceManagerMock();
const factory = new TextModelFactory();
const context = new Context({
manager: manager || new ServiceManagerMock(),
factory,
path,
kernelPreference: {
shouldStart: startKernel,
canStart: startKernel,
autoStartDefault: startKernel
}
});
await context.initialize(true);
await context.sessionContext.initialize();
return context;
}
/**
* Create a session and return a session connection.
*/
export async function createSession(
options: Session.ISessionOptions
): Promise<Session.ISessionConnection> {
const manager = Private.getManager().sessions;
await manager.ready;
return manager.startNew(options);
}
/**
* Create a session context given a partial session model.
*
* @param model The session model to use.
*/
export function createSimpleSessionContext(
model: Private.RecursivePartial<Session.IModel> = {}
): ISessionContext {
const kernel = new KernelMock({ model: model?.kernel || {} });
const session = new SessionConnectionMock({ model }, kernel);
return new SessionContextMock({}, session);
}
/**
* Emit an iopub message on a session context.
*
* @param sessionContext The session context
* @param msg Message created with `KernelMessage.createMessage`
*/
export function emitIopubMessage(
context: ISessionContext,
msg: KernelMessage.IIOPubMessage
): void {
const kernel = context!.session!.kernel!;
const msgId = Private.lastMessageProperty.get(kernel);
(msg.parent_header as any).session = kernel.clientId;
(msg.parent_header as any).msg_id = msgId;
(kernel.iopubMessage as any).emit(msg);
}
/**
* Forcibly change the status of a session context.
* An iopub message is emitted for the change.
*
* @param sessionContext The session context of interest.
* @param newStatus The new kernel status.
*/
export function updateKernelStatus(
sessionContext: ISessionContext,
newStatus: KernelMessage.Status
): void {
const kernel = sessionContext.session!.kernel!;
(kernel as any).status = newStatus;
(sessionContext.statusChanged as any).emit(newStatus);
const msg = KernelMessage.createMessage({
session: kernel.clientId,
channel: 'iopub',
msgType: 'status',
content: { execution_state: newStatus }
});
emitIopubMessage(sessionContext, msg);
}
/**
* A mock session context.
*
* @param session The session connection object to use
*/
export const SessionContextMock = jest.fn<
ISessionContext,
[Partial<SessionContext.IOptions>, Session.ISessionConnection | null]
>((options, connection) => {
const session =
connection ||
new SessionConnectionMock(
{
model: {
path: options.path || '',
type: options.type || '',
name: options.name || ''
}
},
null
);
const thisObject: ISessionContext = {
...jest.requireActual('@jupyterlab/apputils'),
...options,
path: session.path,
type: session.type,
name: session.name,
session,
dispose: jest.fn(),
initialize: jest.fn(() => Promise.resolve(false)),
ready: Promise.resolve(),
changeKernel: jest.fn(partialModel => {
return changeKernel(
session.kernel || Private.RUNNING_KERNELS[0],
partialModel!
);
}),
shutdown: jest.fn(() => Promise.resolve())
};
const disposedSignal = new Signal<ISessionContext, undefined>(thisObject);
const propertyChangedSignal = new Signal<
ISessionContext,
'path' | 'name' | 'type'
>(thisObject);
const statusChangedSignal = new Signal<ISessionContext, Kernel.Status>(
thisObject
);
const kernelChangedSignal = new Signal<
ISessionContext,
Session.ISessionConnection.IKernelChangedArgs
>(thisObject);
const iopubMessageSignal = new Signal<
ISessionContext,
KernelMessage.IIOPubMessage
>(thisObject);
session!.statusChanged.connect((_, args) => {
statusChangedSignal.emit(args);
}, thisObject);
session!.iopubMessage.connect((_, args) => {
iopubMessageSignal.emit(args);
});
session!.kernelChanged.connect((_, args) => {
kernelChangedSignal.emit(args);
});
session!.pendingInput.connect((_, args) => {
(thisObject as any).pendingInput = args;
});
(thisObject as any).statusChanged = statusChangedSignal;
(thisObject as any).kernelChanged = kernelChangedSignal;
(thisObject as any).iopubMessage = iopubMessageSignal;
(thisObject as any).propertyChanged = propertyChangedSignal;
(thisObject as any).disposed = disposedSignal;
(thisObject as any).session = session;
(thisObject as any).pendingInput = false;
return thisObject;
});
/**
* A namespace for module private data.
*/
namespace Private {
export type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>;
};
let manager: ServiceManager;
export const textFactory = new TextModelFactory();
/**
* Get or create the service manager singleton.
*/
export function getManager(): ServiceManager {
if (!manager) {
manager = new ServiceManager({ standby: 'never' });
}
return manager;
}
export const lastMessageProperty = new AttachedProperty<
Kernel.IKernelConnection,
string
>({
name: 'lastMessageId',
create: () => ''
});
// This list of running kernels simply mirrors the KERNEL_MODELS and KERNELSPECS lists
export const RUNNING_KERNELS: Kernel.IKernelConnection[] = KERNEL_MODELS.map(
(model, _) => {
return new KernelMock({ model });
}
);
}
/**
* A mock document widget opener.
*/
export class DocumentWidgetOpenerMock {
get opened(): ISignal<DocumentWidgetOpenerMock, IDocumentWidget> {
return this._opened;
}
open(widget: IDocumentWidget): void {
// no-op, just emit the signal
this._opened.emit(widget);
}
private _opened = new Signal<this, IDocumentWidget>(this);
}