Skip to content

Commit 7eab373

Browse files
committed
Display a success or error message once git-graph.clearAvatarCache has executed.
1 parent cf681bb commit 7eab373

6 files changed

Lines changed: 128 additions & 28 deletions

File tree

src/avatarManager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,11 @@ export class AvatarManager extends Disposable {
134134

135135
/**
136136
* Remove all avatars from the cache.
137+
* @returns A Thenable resolving to the ErrorInfo that resulted from executing this method.
137138
*/
138139
public clearCache() {
139140
this.avatars = {};
140-
this.extensionState.clearAvatarCache();
141+
return this.extensionState.clearAvatarCache();
141142
}
142143

143144
/**

src/commands.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,15 @@ export class CommandManager extends Disposable {
183183
* The method run when the `git-graph.clearAvatarCache` command is invoked.
184184
*/
185185
private clearAvatarCache() {
186-
this.avatarManager.clearCache();
186+
this.avatarManager.clearCache().then((errorInfo) => {
187+
if (errorInfo === null) {
188+
showInformationMessage('The Avatar Cache was successfully cleared.');
189+
} else {
190+
showErrorMessage(errorInfo);
191+
}
192+
}, () => {
193+
showErrorMessage('An unexpected error occurred while running the command "Clear Avatar Cache".');
194+
});
187195
}
188196

189197
/**

src/extensionState.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,19 @@ export class ExtensionState extends Disposable {
297297

298298
/**
299299
* Clear all avatars from the cache of avatars known to Git Graph.
300+
* @returns A Thenable resolving to the ErrorInfo that resulted from executing this method.
300301
*/
301302
public clearAvatarCache() {
302-
this.updateGlobalState(AVATAR_CACHE, {});
303-
fs.readdir(this.globalStoragePath + AVATAR_STORAGE_FOLDER, (err, files) => {
304-
if (err) return;
305-
for (let i = 0; i < files.length; i++) {
306-
fs.unlink(this.globalStoragePath + AVATAR_STORAGE_FOLDER + '/' + files[i], () => { });
303+
return this.updateGlobalState(AVATAR_CACHE, {}).then((errorInfo) => {
304+
if (errorInfo === null) {
305+
fs.readdir(this.globalStoragePath + AVATAR_STORAGE_FOLDER, (err, files) => {
306+
if (err) return;
307+
for (let i = 0; i < files.length; i++) {
308+
fs.unlink(this.globalStoragePath + AVATAR_STORAGE_FOLDER + '/' + files[i], () => { });
309+
}
310+
});
307311
}
312+
return errorInfo;
308313
});
309314
}
310315

@@ -434,6 +439,7 @@ export class ExtensionState extends Disposable {
434439
* Update the Git Graph Global State with a new <key, value> pair.
435440
* @param key The key.
436441
* @param value The value.
442+
* @returns A Thenable resolving to the ErrorInfo that resulted from updating the Global State.
437443
*/
438444
private updateGlobalState(key: string, value: any): Thenable<ErrorInfo> {
439445
return this.globalState.update(key, value).then(
@@ -446,6 +452,7 @@ export class ExtensionState extends Disposable {
446452
* Update the Git Graph Workspace State with a new <key, value> pair.
447453
* @param key The key.
448454
* @param value The value.
455+
* @returns A Thenable resolving to the ErrorInfo that resulted from updating the Workspace State.
449456
*/
450457
private updateWorkspaceState(key: string, value: any): Thenable<ErrorInfo> {
451458
return this.workspaceState.update(key, value).then(

tests/avatarManager.test.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,13 +1627,36 @@ describe('AvatarManager', () => {
16271627
});
16281628

16291629
describe('clearCache', () => {
1630-
it('Should clear the cache of avatars', () => {
1630+
let spyOnClearAvatarCache: jest.SpyInstance;
1631+
beforeAll(() => {
1632+
spyOnClearAvatarCache = jest.spyOn(extensionState, 'clearAvatarCache');
1633+
});
1634+
1635+
it('Should clear the cache of avatars', async () => {
1636+
// Setup
1637+
spyOnClearAvatarCache.mockResolvedValueOnce(null);
1638+
1639+
// Run
1640+
const result = await avatarManager.clearCache();
1641+
1642+
// Assert
1643+
expect(result).toBeNull();
1644+
expect(avatarManager['avatars']).toStrictEqual({});
1645+
expect(spyOnClearAvatarCache).toHaveBeenCalledTimes(1);
1646+
});
1647+
1648+
it('Should return the error message returned by ExtensionState.clearAvatarCache', async () => {
1649+
// Setup
1650+
const errorMessage = 'Visual Studio Code was unable to save the Git Graph Global State Memento.';
1651+
spyOnClearAvatarCache.mockResolvedValueOnce(errorMessage);
1652+
16311653
// Run
1632-
avatarManager.clearCache();
1654+
const result = await avatarManager.clearCache();
16331655

16341656
// Assert
1657+
expect(result).toBe(errorMessage);
16351658
expect(avatarManager['avatars']).toStrictEqual({});
1636-
expect(extensionState.clearAvatarCache).toHaveBeenCalledTimes(1);
1659+
expect(spyOnClearAvatarCache).toHaveBeenCalledTimes(1);
16371660
});
16381661
});
16391662
});

tests/commands.test.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,16 +518,58 @@ describe('CommandManager', () => {
518518
});
519519

520520
describe('git-graph.clearAvatarCache', () => {
521-
it('Should clear the avatar cache', () => {
521+
let spyOnClearCache: jest.SpyInstance;
522+
beforeAll(() => {
523+
spyOnClearCache = jest.spyOn(avatarManager, 'clearCache');
524+
});
525+
526+
it('Should clear the avatar cache, and display a success message', async () => {
527+
// Setup
528+
spyOnClearCache.mockResolvedValueOnce(null);
529+
vscode.window.showInformationMessage.mockResolvedValueOnce(null);
530+
531+
// Run
532+
vscode.commands.executeCommand('git-graph.clearAvatarCache');
533+
534+
// Assert
535+
await waitForExpect(() => {
536+
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
537+
expect(spyOnClearCache).toBeCalledTimes(1);
538+
expect(vscode.window.showInformationMessage).toHaveBeenCalledWith('The Avatar Cache was successfully cleared.');
539+
});
540+
});
541+
542+
it('Should display the error message returned by AvatarManager.clearCache', async () => {
522543
// Setup
523-
const spyOnClearCache = jest.spyOn(avatarManager, 'clearCache');
544+
const errorMessage = 'Visual Studio Code was unable to save the Git Graph Global State Memento.';
545+
spyOnClearCache.mockResolvedValueOnce(errorMessage);
546+
vscode.window.showErrorMessage.mockResolvedValueOnce(null);
547+
548+
// Run
549+
vscode.commands.executeCommand('git-graph.clearAvatarCache');
550+
551+
// Assert
552+
await waitForExpect(() => {
553+
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
554+
expect(spyOnClearCache).toBeCalledTimes(1);
555+
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(errorMessage);
556+
});
557+
});
558+
559+
it('Should display an error message when AvatarManager.clearCache rejects', async () => {
560+
// Setup
561+
spyOnClearCache.mockRejectedValueOnce(null);
562+
vscode.window.showErrorMessage.mockResolvedValueOnce(null);
524563

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

528567
// Assert
529-
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
530-
expect(spyOnClearCache).toBeCalledTimes(1);
568+
await waitForExpect(() => {
569+
expect(spyOnLog).toHaveBeenCalledWith('Command Invoked: git-graph.clearAvatarCache');
570+
expect(spyOnClearCache).toBeCalledTimes(1);
571+
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith('An unexpected error occurred while running the command "Clear Avatar Cache".');
572+
});
531573
});
532574
});
533575

tests/extensionState.test.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -863,41 +863,60 @@ describe('ExtensionState', () => {
863863
});
864864

865865
describe('clearAvatarCache', () => {
866-
it('Should clear all avatars from the cache and delete all avatars that are currently stored on the file system', () => {
866+
let spyOnReaddir: jest.SpyInstance, spyOnUnlink: jest.SpyInstance;
867+
beforeAll(() => {
868+
spyOnReaddir = jest.spyOn(fs, 'readdir');
869+
spyOnUnlink = jest.spyOn(fs, 'unlink');
870+
});
871+
872+
it('Should clear all avatars from the cache and delete all avatars that are currently stored on the file system', async () => {
873+
// Setup
867874
extensionContext.globalState.update.mockResolvedValueOnce(null);
868-
const spyOnReaddir = jest.spyOn(fs, 'readdir');
869875
spyOnReaddir.mockImplementationOnce((_, callback) => callback(null, ['file1.jpg', 'file2.jpg']));
870-
const spyOnUnlink = jest.spyOn(fs, 'unlink');
871-
spyOnUnlink.mockImplementation((_, callback) => callback(null));
876+
spyOnUnlink.mockImplementationOnce((_, callback) => callback(null));
877+
spyOnUnlink.mockImplementationOnce((_, callback) => callback(null));
872878

873879
// Run
874-
extensionState.clearAvatarCache();
880+
const result = await extensionState.clearAvatarCache();
875881

876882
// Assert
883+
expect(result).toBeNull();
877884
expect(extensionContext.globalState.update).toHaveBeenCalledWith('avatarCache', {});
878885
expect(spyOnReaddir).toHaveBeenCalledTimes(1);
879-
expect(spyOnReaddir.mock.calls[0][0]).toBe('/path/to/globalStorage/avatars');
886+
expect(spyOnReaddir).toHaveBeenNthCalledWith(1, '/path/to/globalStorage/avatars', expect.anything());
880887
expect(spyOnUnlink).toHaveBeenCalledTimes(2);
881-
expect(spyOnUnlink.mock.calls[0][0]).toBe('/path/to/globalStorage/avatars/file1.jpg');
882-
expect(spyOnUnlink.mock.calls[1][0]).toBe('/path/to/globalStorage/avatars/file2.jpg');
888+
expect(spyOnUnlink).toHaveBeenNthCalledWith(1, '/path/to/globalStorage/avatars/file1.jpg', expect.anything());
889+
expect(spyOnUnlink).toHaveBeenNthCalledWith(2, '/path/to/globalStorage/avatars/file2.jpg', expect.anything());
883890
});
884891

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

892897
// Run
893-
extensionState.clearAvatarCache();
898+
const result = await extensionState.clearAvatarCache();
894899

895900
// Assert
901+
expect(result).toBeNull();
896902
expect(extensionContext.globalState.update).toHaveBeenCalledWith('avatarCache', {});
897903
expect(spyOnReaddir).toHaveBeenCalledTimes(1);
898-
expect(spyOnReaddir.mock.calls[0][0]).toBe('/path/to/globalStorage/avatars');
904+
expect(spyOnReaddir).toHaveBeenNthCalledWith(1, '/path/to/globalStorage/avatars', expect.anything());
899905
expect(spyOnUnlink).toHaveBeenCalledTimes(0);
900906
});
907+
908+
it('Shouldn\'t delete avatars on the file system if globalState.update rejects, and return the error message', async () => {
909+
// Setup
910+
extensionContext.globalState.update.mockRejectedValueOnce(null);
911+
912+
// Run
913+
const result = await extensionState.clearAvatarCache();
914+
915+
// Assert
916+
expect(result).toBe('Visual Studio Code was unable to save the Git Graph Global State Memento.');
917+
expect(extensionContext.globalState.update).toHaveBeenCalledWith('avatarCache', {});
918+
expect(spyOnReaddir).not.toHaveBeenCalled();
919+
});
901920
});
902921

903922
describe('startCodeReview', () => {

0 commit comments

Comments
 (0)