Skip to content

Commit

Permalink
Display a success or error message once git-graph.clearAvatarCache
Browse files Browse the repository at this point in the history
…has executed.
  • Loading branch information
mhutchie committed Apr 10, 2021
1 parent cf681bb commit 7eab373
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 28 deletions.
3 changes: 2 additions & 1 deletion src/avatarManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,11 @@ export class AvatarManager extends Disposable {

/**
* Remove all avatars from the cache.
* @returns A Thenable resolving to the ErrorInfo that resulted from executing this method.
*/
public clearCache() {
this.avatars = {};
this.extensionState.clearAvatarCache();
return this.extensionState.clearAvatarCache();
}

/**
Expand Down
10 changes: 9 additions & 1 deletion src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,15 @@ export class CommandManager extends Disposable {
* The method run when the `git-graph.clearAvatarCache` command is invoked.
*/
private clearAvatarCache() {
this.avatarManager.clearCache();
this.avatarManager.clearCache().then((errorInfo) => {
if (errorInfo === null) {
showInformationMessage('The Avatar Cache was successfully cleared.');
} else {
showErrorMessage(errorInfo);
}
}, () => {
showErrorMessage('An unexpected error occurred while running the command "Clear Avatar Cache".');
});
}

/**
Expand Down
17 changes: 12 additions & 5 deletions src/extensionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,19 @@ export class ExtensionState extends Disposable {

/**
* Clear all avatars from the cache of avatars known to Git Graph.
* @returns A Thenable resolving to the ErrorInfo that resulted from executing this method.
*/
public clearAvatarCache() {
this.updateGlobalState(AVATAR_CACHE, {});
fs.readdir(this.globalStoragePath + AVATAR_STORAGE_FOLDER, (err, files) => {
if (err) return;
for (let i = 0; i < files.length; i++) {
fs.unlink(this.globalStoragePath + AVATAR_STORAGE_FOLDER + '/' + files[i], () => { });
return this.updateGlobalState(AVATAR_CACHE, {}).then((errorInfo) => {
if (errorInfo === null) {
fs.readdir(this.globalStoragePath + AVATAR_STORAGE_FOLDER, (err, files) => {
if (err) return;
for (let i = 0; i < files.length; i++) {
fs.unlink(this.globalStoragePath + AVATAR_STORAGE_FOLDER + '/' + files[i], () => { });
}
});
}
return errorInfo;
});
}

Expand Down Expand Up @@ -434,6 +439,7 @@ export class ExtensionState extends Disposable {
* Update the Git Graph Global State with a new <key, value> pair.
* @param key The key.
* @param value The value.
* @returns A Thenable resolving to the ErrorInfo that resulted from updating the Global State.
*/
private updateGlobalState(key: string, value: any): Thenable<ErrorInfo> {
return this.globalState.update(key, value).then(
Expand All @@ -446,6 +452,7 @@ export class ExtensionState extends Disposable {
* Update the Git Graph Workspace State with a new <key, value> pair.
* @param key The key.
* @param value The value.
* @returns A Thenable resolving to the ErrorInfo that resulted from updating the Workspace State.
*/
private updateWorkspaceState(key: string, value: any): Thenable<ErrorInfo> {
return this.workspaceState.update(key, value).then(
Expand Down
29 changes: 26 additions & 3 deletions tests/avatarManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1627,13 +1627,36 @@ describe('AvatarManager', () => {
});

describe('clearCache', () => {
it('Should clear the cache of avatars', () => {
let spyOnClearAvatarCache: jest.SpyInstance;
beforeAll(() => {
spyOnClearAvatarCache = jest.spyOn(extensionState, 'clearAvatarCache');
});

it('Should clear the cache of avatars', async () => {
// Setup
spyOnClearAvatarCache.mockResolvedValueOnce(null);

// Run
const result = await avatarManager.clearCache();

// Assert
expect(result).toBeNull();
expect(avatarManager['avatars']).toStrictEqual({});
expect(spyOnClearAvatarCache).toHaveBeenCalledTimes(1);
});

it('Should return the error message returned by ExtensionState.clearAvatarCache', async () => {
// Setup
const errorMessage = 'Visual Studio Code was unable to save the Git Graph Global State Memento.';
spyOnClearAvatarCache.mockResolvedValueOnce(errorMessage);

// Run
avatarManager.clearCache();
const result = await avatarManager.clearCache();

// Assert
expect(result).toBe(errorMessage);
expect(avatarManager['avatars']).toStrictEqual({});
expect(extensionState.clearAvatarCache).toHaveBeenCalledTimes(1);
expect(spyOnClearAvatarCache).toHaveBeenCalledTimes(1);
});
});
});
Expand Down
50 changes: 46 additions & 4 deletions tests/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,16 +518,58 @@ describe('CommandManager', () => {
});

describe('git-graph.clearAvatarCache', () => {
it('Should clear the avatar cache', () => {
let spyOnClearCache: jest.SpyInstance;
beforeAll(() => {
spyOnClearCache = jest.spyOn(avatarManager, 'clearCache');
});

it('Should clear the avatar cache, and display a success message', async () => {
// Setup
spyOnClearCache.mockResolvedValueOnce(null);
vscode.window.showInformationMessage.mockResolvedValueOnce(null);

// Run
vscode.commands.executeCommand('git-graph.clearAvatarCache');

// Assert
await waitForExpect(() => {
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
expect(spyOnClearCache).toBeCalledTimes(1);
expect(vscode.window.showInformationMessage).toHaveBeenCalledWith('The Avatar Cache was successfully cleared.');
});
});

it('Should display the error message returned by AvatarManager.clearCache', async () => {
// Setup
const spyOnClearCache = jest.spyOn(avatarManager, 'clearCache');
const errorMessage = 'Visual Studio Code was unable to save the Git Graph Global State Memento.';
spyOnClearCache.mockResolvedValueOnce(errorMessage);
vscode.window.showErrorMessage.mockResolvedValueOnce(null);

// Run
vscode.commands.executeCommand('git-graph.clearAvatarCache');

// Assert
await waitForExpect(() => {
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
expect(spyOnClearCache).toBeCalledTimes(1);
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(errorMessage);
});
});

it('Should display an error message when AvatarManager.clearCache rejects', async () => {
// Setup
spyOnClearCache.mockRejectedValueOnce(null);
vscode.window.showErrorMessage.mockResolvedValueOnce(null);

// Run
vscode.commands.executeCommand('git-graph.clearAvatarCache');

// Assert
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
expect(spyOnClearCache).toBeCalledTimes(1);
await waitForExpect(() => {
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
expect(spyOnClearCache).toBeCalledTimes(1);
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith('An unexpected error occurred while running the command "Clear Avatar Cache".');
});
});
});

Expand Down
47 changes: 33 additions & 14 deletions tests/extensionState.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -863,41 +863,60 @@ describe('ExtensionState', () => {
});

describe('clearAvatarCache', () => {
it('Should clear all avatars from the cache and delete all avatars that are currently stored on the file system', () => {
let spyOnReaddir: jest.SpyInstance, spyOnUnlink: jest.SpyInstance;
beforeAll(() => {
spyOnReaddir = jest.spyOn(fs, 'readdir');
spyOnUnlink = jest.spyOn(fs, 'unlink');
});

it('Should clear all avatars from the cache and delete all avatars that are currently stored on the file system', async () => {
// Setup
extensionContext.globalState.update.mockResolvedValueOnce(null);
const spyOnReaddir = jest.spyOn(fs, 'readdir');
spyOnReaddir.mockImplementationOnce((_, callback) => callback(null, ['file1.jpg', 'file2.jpg']));
const spyOnUnlink = jest.spyOn(fs, 'unlink');
spyOnUnlink.mockImplementation((_, callback) => callback(null));
spyOnUnlink.mockImplementationOnce((_, callback) => callback(null));
spyOnUnlink.mockImplementationOnce((_, callback) => callback(null));

// Run
extensionState.clearAvatarCache();
const result = await extensionState.clearAvatarCache();

// Assert
expect(result).toBeNull();
expect(extensionContext.globalState.update).toHaveBeenCalledWith('avatarCache', {});
expect(spyOnReaddir).toHaveBeenCalledTimes(1);
expect(spyOnReaddir.mock.calls[0][0]).toBe('/path/to/globalStorage/avatars');
expect(spyOnReaddir).toHaveBeenNthCalledWith(1, '/path/to/globalStorage/avatars', expect.anything());
expect(spyOnUnlink).toHaveBeenCalledTimes(2);
expect(spyOnUnlink.mock.calls[0][0]).toBe('/path/to/globalStorage/avatars/file1.jpg');
expect(spyOnUnlink.mock.calls[1][0]).toBe('/path/to/globalStorage/avatars/file2.jpg');
expect(spyOnUnlink).toHaveBeenNthCalledWith(1, '/path/to/globalStorage/avatars/file1.jpg', expect.anything());
expect(spyOnUnlink).toHaveBeenNthCalledWith(2, '/path/to/globalStorage/avatars/file2.jpg', expect.anything());
});

it('Should skip deleting avatars on the file system if they could not be listed from the file system', () => {
it('Should skip deleting avatars on the file system if they could not be listed from the file system', async () => {
// Setup
extensionContext.globalState.update.mockResolvedValueOnce(null);
const spyOnReaddir = jest.spyOn(fs, 'readdir');
spyOnReaddir.mockImplementationOnce((_, callback) => callback(new Error(), ['file1.jpg', 'file2.jpg']));
const spyOnUnlink = jest.spyOn(fs, 'unlink');
spyOnUnlink.mockImplementation((_, callback) => callback(null));

// Run
extensionState.clearAvatarCache();
const result = await extensionState.clearAvatarCache();

// Assert
expect(result).toBeNull();
expect(extensionContext.globalState.update).toHaveBeenCalledWith('avatarCache', {});
expect(spyOnReaddir).toHaveBeenCalledTimes(1);
expect(spyOnReaddir.mock.calls[0][0]).toBe('/path/to/globalStorage/avatars');
expect(spyOnReaddir).toHaveBeenNthCalledWith(1, '/path/to/globalStorage/avatars', expect.anything());
expect(spyOnUnlink).toHaveBeenCalledTimes(0);
});

it('Shouldn\'t delete avatars on the file system if globalState.update rejects, and return the error message', async () => {
// Setup
extensionContext.globalState.update.mockRejectedValueOnce(null);

// Run
const result = await extensionState.clearAvatarCache();

// Assert
expect(result).toBe('Visual Studio Code was unable to save the Git Graph Global State Memento.');
expect(extensionContext.globalState.update).toHaveBeenCalledWith('avatarCache', {});
expect(spyOnReaddir).not.toHaveBeenCalled();
});
});

describe('startCodeReview', () => {
Expand Down

0 comments on commit 7eab373

Please sign in to comment.