Преглед изворни кода

Merge pull request #82 from blink1073/improved-mock-manager

Improve the mock manager
Dave Willmer пре 9 година
родитељ
комит
4584a27e4a

+ 2 - 0
test/src/filehandler/default.spec.ts

@@ -85,6 +85,7 @@ describe('jupyter-ui', () => {
       it('should load text and the appropriate codemirror mode', (done) => {
         let manager = new MockContentsManager();
         let handler = new MyFileHandler(manager);
+        manager.createFile('foo.ts');
         let widget = handler.open('foo.ts');
         handler.finished.connect(() => {
           expect(handler.methods.indexOf('populateWidget')).to.not.be(-1);
@@ -104,6 +105,7 @@ describe('jupyter-ui', () => {
       it('should save as a text file', () => {
         let manager = new MockContentsManager();
         let handler = new MyFileHandler(manager);
+        manager.createFile('foo.ts');
         let widget = handler.open('foo.ts');
         widget.editor.getDoc().setValue('test test');
         handler.save('foo.ts').then(contents => {

+ 37 - 0
test/src/filehandler/filehandler.spec.ts

@@ -91,6 +91,7 @@ describe('jupyter-ui', () => {
           expect(widget instanceof Widget).to.be(true);
           called = true;
         });
+        manager.createFile('foo.txt');
         handler.open('foo.txt');
         expect(called).to.be(true);
       });
@@ -106,6 +107,7 @@ describe('jupyter-ui', () => {
           expect(widget instanceof Widget).to.be(true);
           done();
         });
+        manager.createFile('foo.txt');
         handler.open('foo.txt');
       });
 
@@ -132,6 +134,7 @@ describe('jupyter-ui', () => {
       it('should find a widget given a path', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(handler.findWidget('foo.txt')).to.be(widget);
       });
@@ -139,6 +142,7 @@ describe('jupyter-ui', () => {
       it('should return `undefined` if the path is invalid', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(handler.findWidget('bar.txt')).to.be(void 0);
       });
@@ -150,6 +154,7 @@ describe('jupyter-ui', () => {
       it('should find a path given a widget', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(handler.findPath(widget)).to.be('foo.txt');
       });
@@ -157,6 +162,7 @@ describe('jupyter-ui', () => {
       it('should return `undefined` if the widget is invalid', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.close('foo.txt').then(() => {
           expect(handler.findPath(widget)).to.be(void 0);
@@ -171,6 +177,7 @@ describe('jupyter-ui', () => {
       it('should open a file by path and return a widget', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(widget instanceof Widget).to.be(true);
       });
@@ -178,6 +185,7 @@ describe('jupyter-ui', () => {
       it('should return an existing widget if it is already open', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(handler.open('foo.txt')).to.be(widget);
       });
@@ -185,6 +193,7 @@ describe('jupyter-ui', () => {
       it('should clear the dirty state when finished', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.finished.connect(() => {
           expect(handler.isDirty('foo.txt')).to.be(false);
@@ -195,6 +204,7 @@ describe('jupyter-ui', () => {
       it('should set the title', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(widget.title.text).to.be('foo.txt');
       });
@@ -206,6 +216,7 @@ describe('jupyter-ui', () => {
       it('should rename the file', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         let result = handler.rename('foo.txt', 'bar.txt');
         expect(result).to.be(true);
@@ -215,6 +226,7 @@ describe('jupyter-ui', () => {
       it('should update the title', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.rename('foo.txt', 'bar.txt');
         expect(widget.title.text).to.be('bar.txt');
@@ -230,6 +242,7 @@ describe('jupyter-ui', () => {
       it('should close the widget if the new path is undefined', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         let result = handler.rename('foo.txt');
         expect(result).to.be(true);
@@ -243,6 +256,7 @@ describe('jupyter-ui', () => {
       it('should resolve to the file contents', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.save('foo.txt').then(contents => {
           expect(contents.content).to.be('baz');
@@ -253,6 +267,7 @@ describe('jupyter-ui', () => {
       it('should clear the dirty flag', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.setDirty('foo.txt', true);
         handler.save('foo.txt').then(contents => {
@@ -277,6 +292,7 @@ describe('jupyter-ui', () => {
       it('should resolve to the original file contents', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.revert('foo.txt').then(contents => {
           expect(contents.content).to.be(manager.DEFAULT_TEXT);
@@ -287,6 +303,7 @@ describe('jupyter-ui', () => {
       it('should clear the dirty flag', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         handler.setDirty('foo.txt', true);
         handler.revert('foo.txt').then(contents => {
@@ -311,6 +328,7 @@ describe('jupyter-ui', () => {
       it('should close a file by path', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         widget.attach(document.body);
         handler.close('foo.txt').then(result => {
@@ -332,6 +350,7 @@ describe('jupyter-ui', () => {
       it('should prompt the user if the file is dirty', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         handler.open('foo.txt');
         handler.setDirty('foo.txt', true);
         handler.close('foo.txt').then(result => {
@@ -344,6 +363,7 @@ describe('jupyter-ui', () => {
       it('should not close if the user dismisses the dialog', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         handler.open('foo.txt');
         handler.setDirty('foo.txt', true);
         handler.close('foo.txt').then(result => {
@@ -360,6 +380,8 @@ describe('jupyter-ui', () => {
       it('should class all files', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
+        manager.createFile('bar.txt');
         let widget0 = handler.open('foo.txt');
         let widget1 = handler.open('bar.txt');
         widget0.attach(document.body);
@@ -377,6 +399,7 @@ describe('jupyter-ui', () => {
       it('should default to false', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         expect(handler.isDirty('foo.txt')).to.be(false);
       });
@@ -384,6 +407,7 @@ describe('jupyter-ui', () => {
       it('should return `undefined` if the path is invalid', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         expect(handler.isDirty('bar.txt')).to.be(void 0);
       });
@@ -395,6 +419,7 @@ describe('jupyter-ui', () => {
       it('should set the dirty state of a file', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         handler.setDirty('foo.txt', true);
         expect(handler.isDirty('foo.txt')).to.be(true);
@@ -405,6 +430,7 @@ describe('jupyter-ui', () => {
       it('should affect the className of the title', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         expect(widget.title.className.indexOf('jp-mod-dirty')).to.be(-1);
         handler.setDirty('foo.txt', true);
@@ -414,6 +440,7 @@ describe('jupyter-ui', () => {
       it('should be a no-op for an invalid path', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         handler.setDirty('bar.txt', true);
       });
@@ -425,6 +452,7 @@ describe('jupyter-ui', () => {
       it('should filter close messages for contained widgets', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         let value = handler.filterMessage(widget, Widget.MsgCloseRequest);
         expect(value).to.be(true);
@@ -439,6 +467,7 @@ describe('jupyter-ui', () => {
       it('should get the options use to fetch contents from disk', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         expect(handler.methods.indexOf('getFetchOptions')).to.not.be(-1);
       });
@@ -446,6 +475,7 @@ describe('jupyter-ui', () => {
       it('should be called during a revert', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         handler.methods = [];
         handler.revert('foo.txt');
@@ -459,6 +489,7 @@ describe('jupyter-ui', () => {
       it('should get the options used to save the widget', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         handler.save('foo.txt');
         expect(handler.methods.indexOf('getSaveOptions')).to.not.be(-1);
@@ -471,6 +502,7 @@ describe('jupyter-ui', () => {
       it('should be used to create the initial widget given a path', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         expect(handler.methods.indexOf('createWidget')).to.not.be(-1);
       });
@@ -482,6 +514,7 @@ describe('jupyter-ui', () => {
       it('should be called to populate a widget while opening', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         handler.finished.connect(() => {
           expect(handler.methods.indexOf('populateWidget')).to.not.be(-1);
@@ -492,6 +525,7 @@ describe('jupyter-ui', () => {
       it('should be called when reverting', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         let called = false;
         handler.finished.connect(() => {
@@ -510,6 +544,7 @@ describe('jupyter-ui', () => {
       it('should set the appropriate title text based on a path', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         expect(handler.methods.indexOf('getTitleText')).to.not.be(-1);
       });
@@ -517,6 +552,7 @@ describe('jupyter-ui', () => {
       it('should be called when renaming', () => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget0 = handler.open('foo.txt');
         handler.methods = [];
         handler.rename('foo.txt', 'bar.txt');
@@ -529,6 +565,7 @@ describe('jupyter-ui', () => {
       it('should call before closing', (done) => {
         let manager = new MockContentsManager();
         let handler = new FileHandler(manager);
+        manager.createFile('foo.txt');
         let widget = handler.open('foo.txt');
         widget.attach(document.body);
         handler.close('foo.txt').then(result => {

+ 19 - 0
test/src/filehandler/registry.spec.ts

@@ -89,6 +89,7 @@ describe('jupyter-ui', () => {
           expect(widget instanceof Widget).to.be(true);
           called = true;
         });
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
         expect(called).to.be(true);
       });
@@ -106,6 +107,7 @@ describe('jupyter-ui', () => {
           expect(widget instanceof Widget).to.be(true);
           done();
         });
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
       });
 
@@ -135,6 +137,7 @@ describe('jupyter-ui', () => {
         let handler = new FileHandler(manager);
         let registry = new MyRegistry();
         registry.addHandler(handler);
+        manager.createFile('foo.txt');
         let value = registry.open('foo.txt');
         expect(value).to.be(void 0);
       });
@@ -144,6 +147,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addHandler(handler);
+        manager.createFile('foo.txt');
         let value = registry.open('foo.txt');
         expect(value instanceof Widget).to.be(true);
       });
@@ -157,6 +161,7 @@ describe('jupyter-ui', () => {
         let handler = new FileHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         let value = registry.open('foo.txt');
         expect(value instanceof Widget).to.be(true);
       });
@@ -168,6 +173,7 @@ describe('jupyter-ui', () => {
         let main = new FileHandler(manager);
         registry.addDefaultHandler(main);
         registry.addHandler(handler);
+        manager.createFile('foo.txt');
         let value = registry.open('foo.txt');
         expect(handler.methods.indexOf('open')).to.not.be(-1);
       });
@@ -258,6 +264,7 @@ describe('jupyter-ui', () => {
         registry.opened.connect(() => {
           called = true;
         });
+        manager.createFile('foo.txt');
         let value = registry.open('foo.txt');
         expect(value instanceof Widget).to.be(true);
         registry.finished.connect((r, w) => {
@@ -271,6 +278,7 @@ describe('jupyter-ui', () => {
         let manager = new MockContentsManager();
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
+        manager.createFile('foo.txt');
         let value = registry.open('foo.txt');
         expect(value).to.be(void 0);
       });
@@ -284,6 +292,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         let widget = registry.open('foo.txt');
         let response = registry.rename('foo.txt', 'bar.txt');
         expect(widget.title.text).to.be('bar.txt');
@@ -307,6 +316,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
         registry.save('foo.txt').then(contents => {
           expect(contents.name).to.be('foo.txt')
@@ -332,6 +342,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
         registry.revert('foo.txt').then(contents => {
           expect(contents.name).to.be('foo.txt');
@@ -358,6 +369,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
         registry.close('foo.txt').then(value => {
           expect(value).to.be(true);
@@ -385,6 +397,8 @@ describe('jupyter-ui', () => {
         let main = new FileHandler(manager);
         registry.addDefaultHandler(main);
         registry.addHandler(handler);
+        manager.createFile('foo.txt');
+        manager.createFile('foo.md');
         let widget0 = registry.open('foo.txt');
         let widget1 = registry.open('foo.md');
         registry.closeAll().then(() => {
@@ -403,6 +417,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         let widget = registry.open('foo.txt');
         expect(registry.findPath(widget)).to.be('foo.txt');
       });
@@ -422,6 +437,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         let widget = registry.open('foo.txt');
         expect(registry.findWidget('foo.txt')).to.be(widget);
       });
@@ -440,6 +456,7 @@ describe('jupyter-ui', () => {
         let handler = new MyHandler(manager);
         let registry = new MyRegistry();
         registry.addHandler(handler);
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
         expect(registry.methods.indexOf('findHandler')).to.not.be(-1);
         expect(registry.handlers.indexOf(handler)).to.not.be(-1);
@@ -452,6 +469,7 @@ describe('jupyter-ui', () => {
         let registry = new MyRegistry();
         registry.addHandler(handler);
         registry.addDefaultHandler(main);
+        manager.createFile('foo.md');
         registry.open('foo.md');
         expect(registry.methods.indexOf('findHandler')).to.not.be(-1);
         expect(registry.handlers.indexOf(main)).to.not.be(-1);
@@ -464,6 +482,7 @@ describe('jupyter-ui', () => {
         let registry = new MyRegistry();
         registry.addHandler(handler);
         registry.addDefaultHandler(handler);
+        manager.createFile('foo.txt');
         registry.open('foo.txt');
         expect(registry.methods.indexOf('findHandler')).to.not.be(-1);
         expect(registry.handlers.indexOf(handler)).to.not.be(-1);

+ 80 - 25
test/src/mock.ts

@@ -15,14 +15,29 @@ class MockContentsManager implements IContentsManager {
 
   DEFAULT_TEXT = 'the quick brown fox jumped over the lazy dog';
 
-  get(path: string, options?: IContentsOpts): Promise<IContentsModel> {
-    this.methods.push('get');
-    return Promise.resolve({
+  /**
+   * Create a file with default content.
+   */
+  createFile(path: string): void {
+    let model = {
       name: path.split('/').pop(),
       path: path,
       type: 'file',
       content: this.DEFAULT_TEXT
-    });
+    }
+    this._files[path] = model;
+  }
+
+  /**
+   * Get a path in the format it was saved or created in.
+   */
+  get(path: string, options?: IContentsOpts): Promise<IContentsModel> {
+    this.methods.push('get');
+    let model = this._files[path];
+    if (!model) {
+      return Promise.reject(new Error('Path not found'));
+    }
+    return Promise.resolve(this._copyModel(model));
   }
 
   newUntitled(path: string, options?: IContentsOpts): Promise<IContentsModel> {
@@ -30,76 +45,116 @@ class MockContentsManager implements IContentsManager {
     options = options || {};
     let ext = options.ext || '';
     let name = options.name || `untitled${ext}`;
-    return Promise.resolve({
+    path = `${path}/${name}`;
+    let model = {
       name,
-      path: `${path}/${name}`,
+      path,
       format: options.format || 'text',
       type: options.type || 'file',
-      ext,
       content: options.content || this.DEFAULT_TEXT
-    });
+    };
+    this._files[path] = model;
+    return Promise.resolve(this._copyModel(model));
   }
 
   delete(path: string): Promise<void> {
     this.methods.push('delete');
+    delete this._files[path];
     return Promise.resolve(void 0);
   }
 
   rename(path: string, newPath: string): Promise<IContentsModel> {
     this.methods.push('rename');
-    return Promise.resolve({
-      name: newPath.split('/').pop(),
-      path: newPath,
-      type: 'file',
-      content: this.DEFAULT_TEXT
-    });
+    let model = this._files[path];
+    if (!model) {
+      return Promise.reject(new Error('Path not found'));
+    }
+    model.name = newPath.split('/').pop();
+    model.path = newPath;
+    delete this._files[path];
+    this._files[newPath] = model;
+    return Promise.resolve(model);
   }
 
   save(path: string, model: IContentsModel): Promise<IContentsModel> {
     this.methods.push('save');
+    this._files[path] = this._copyModel(model);
     return Promise.resolve(model);
   }
 
   copy(path: string, toDir: string): Promise<IContentsModel> {
     this.methods.push('copy');
-    let name = path.split('/').pop();
-    return Promise.resolve({
-      name,
-      path: `${toDir}/${name}`,
-      type: 'file',
-      content: this.DEFAULT_TEXT
-    });
+    let model = this._files[path];
+    if (!model) {
+      return Promise.reject(new Error('Path not found'));
+    }
+    let newModel = JSON.parse(JSON.stringify(model)) as IContentsModel;
+    newModel.path = `${toDir}/${model.name}`;
+    this._files[newModel.path] = newModel;
+    return Promise.resolve(newModel);
   }
 
   listContents(path: string): Promise<IContentsModel> {
     this.methods.push('listContents');
+    let files: IContentsModel[] = [];
+    for (let key of Object.keys(this._files)) {
+      let model = this._files[key];
+      let dname = model.path.slice(0, model.name.length);
+      if (dname === path) {
+        files.push(model);
+      }
+    }
     return Promise.resolve({
       name: path.split('/').pop(),
       path,
-      type: 'dirty',
-      content: []
+      type: 'directory',
+      content: files
     });
   }
 
   createCheckpoint(path: string): Promise<ICheckpointModel> {
     this.methods.push('createCheckpoint');
-    return Promise.resolve(void 0);
+    let fileModel = this._files[path];
+    if (!fileModel) {
+      return Promise.reject(new Error('Path not found'));
+    }
+    let checkpoints: ICheckpointModel[] = this._checkpoints[path] || [];
+    let id = String(this._id++);
+    let date = new Date(Date.now());
+    let last_modified = date.toISOString();
+    let model: ICheckpointModel = { id, last_modified };
+    checkpoints.push(model);
+    this._checkpoints[path] = checkpoints;
+    this._fileSnaps[id] = this._copyModel(fileModel);
+    return Promise.resolve(model);
   }
 
   listCheckpoints(path: string): Promise<ICheckpointModel[]> {
     this.methods.push('listCheckpoints');
-    return Promise.resolve(void 0);
+    let checkpoints: ICheckpointModel[] = this._checkpoints[path] || [];
+    return Promise.resolve(checkpoints);
   }
 
   restoreCheckpoint(path: string, checkpointID: string): Promise<void> {
     this.methods.push('restoreCheckpoint');
+    this._files[path] = this._copyModel(this._fileSnaps[checkpointID]);
     return Promise.resolve(void 0);
   }
 
   deleteCheckpoint(path: string, checkpointID: string): Promise<void> {
     this.methods.push('deleteCheckpoint');
+    delete this._fileSnaps[checkpointID];
     return Promise.resolve(void 0);
   }
 
+  private _copyModel(model: IContentsModel): IContentsModel {
+    return JSON.parse(JSON.stringify(model)) as IContentsModel;
+  }
+
   ajaxSettings: IAjaxSettings = {};
+
+  private _files: { [key: string]: IContentsModel } = Object.create(null);
+  private _checkpoints: { [key: string]: ICheckpointModel[] } = Object.create(null);
+  private _fileSnaps: { [key: string]: IContentsModel } = Object.create(null);
+  private _id = 0;
 }