Switch to a reducer for tracking update state fixing flicker issues due to continuous renders (#10280)

This commit is contained in:
Jacob Richman
2025-10-01 14:53:15 -07:00
committed by GitHub
parent ef76a801c4
commit a404fb8d2e
13 changed files with 599 additions and 361 deletions
@@ -185,8 +185,7 @@ describe('update tests', () => {
});
mockGit.getRemotes.mockResolvedValue([{ name: 'origin' }]);
const setExtensionUpdateState = vi.fn();
const dispatch = vi.fn();
const extension = annotateActiveExtensions(
[
loadExtension({
@@ -202,15 +201,23 @@ describe('update tests', () => {
tempHomeDir,
async (_) => true,
ExtensionUpdateState.UPDATE_AVAILABLE,
setExtensionUpdateState,
dispatch,
);
expect(setExtensionUpdateState).toHaveBeenCalledWith(
ExtensionUpdateState.UPDATING,
);
expect(setExtensionUpdateState).toHaveBeenCalledWith(
ExtensionUpdateState.UPDATED_NEEDS_RESTART,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: extensionName,
state: ExtensionUpdateState.UPDATING,
},
});
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: extensionName,
state: ExtensionUpdateState.UPDATED_NEEDS_RESTART,
},
});
});
it('should call setExtensionUpdateState with ERROR on failure', async () => {
@@ -228,7 +235,7 @@ describe('update tests', () => {
mockGit.clone.mockRejectedValue(new Error('Git clone failed'));
mockGit.getRemotes.mockResolvedValue([{ name: 'origin' }]);
const setExtensionUpdateState = vi.fn();
const dispatch = vi.fn();
const extension = annotateActiveExtensions(
[
loadExtension({
@@ -245,16 +252,24 @@ describe('update tests', () => {
tempHomeDir,
async (_) => true,
ExtensionUpdateState.UPDATE_AVAILABLE,
setExtensionUpdateState,
dispatch,
),
).rejects.toThrow();
expect(setExtensionUpdateState).toHaveBeenCalledWith(
ExtensionUpdateState.UPDATING,
);
expect(setExtensionUpdateState).toHaveBeenCalledWith(
ExtensionUpdateState.ERROR,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: extensionName,
state: ExtensionUpdateState.UPDATING,
},
});
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: extensionName,
state: ExtensionUpdateState.ERROR,
},
});
});
});
@@ -286,20 +301,15 @@ describe('update tests', () => {
mockGit.listRemote.mockResolvedValue('remoteHash HEAD');
mockGit.revparse.mockResolvedValue('localHash');
let extensionState = new Map();
const results = await checkForAllExtensionUpdates(
[extension],
extensionState,
(newState) => {
if (typeof newState === 'function') {
newState(extensionState);
} else {
extensionState = newState;
}
const dispatch = vi.fn();
await checkForAllExtensionUpdates([extension], dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: 'test-extension',
state: ExtensionUpdateState.UPDATE_AVAILABLE,
},
);
const result = results.get('test-extension');
expect(result).toBe(ExtensionUpdateState.UPDATE_AVAILABLE);
});
});
it('should return UpToDate for a git extension with no updates', async () => {
@@ -329,20 +339,15 @@ describe('update tests', () => {
mockGit.listRemote.mockResolvedValue('sameHash HEAD');
mockGit.revparse.mockResolvedValue('sameHash');
let extensionState = new Map();
const results = await checkForAllExtensionUpdates(
[extension],
extensionState,
(newState) => {
if (typeof newState === 'function') {
newState(extensionState);
} else {
extensionState = newState;
}
const dispatch = vi.fn();
await checkForAllExtensionUpdates([extension], dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: 'test-extension',
state: ExtensionUpdateState.UP_TO_DATE,
},
);
const result = results.get('test-extension');
expect(result).toBe(ExtensionUpdateState.UP_TO_DATE);
});
});
it('should return UpToDate for a local extension with no updates', async () => {
@@ -369,21 +374,15 @@ describe('update tests', () => {
process.cwd(),
new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir()),
)[0];
let extensionState = new Map();
const results = await checkForAllExtensionUpdates(
[extension],
extensionState,
(newState) => {
if (typeof newState === 'function') {
newState(extensionState);
} else {
extensionState = newState;
}
const dispatch = vi.fn();
await checkForAllExtensionUpdates([extension], dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: 'local-extension',
state: ExtensionUpdateState.UP_TO_DATE,
},
tempWorkspaceDir,
);
const result = results.get('local-extension');
expect(result).toBe(ExtensionUpdateState.UP_TO_DATE);
});
});
it('should return UpdateAvailable for a local extension with updates', async () => {
@@ -410,21 +409,15 @@ describe('update tests', () => {
process.cwd(),
new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir()),
)[0];
let extensionState = new Map();
const results = await checkForAllExtensionUpdates(
[extension],
extensionState,
(newState) => {
if (typeof newState === 'function') {
newState(extensionState);
} else {
extensionState = newState;
}
const dispatch = vi.fn();
await checkForAllExtensionUpdates([extension], dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: 'local-extension',
state: ExtensionUpdateState.UPDATE_AVAILABLE,
},
tempWorkspaceDir,
);
const result = results.get('local-extension');
expect(result).toBe(ExtensionUpdateState.UPDATE_AVAILABLE);
});
});
it('should return Error when git check fails', async () => {
@@ -450,20 +443,15 @@ describe('update tests', () => {
mockGit.getRemotes.mockRejectedValue(new Error('Git error'));
let extensionState = new Map();
const results = await checkForAllExtensionUpdates(
[extension],
extensionState,
(newState) => {
if (typeof newState === 'function') {
newState(extensionState);
} else {
extensionState = newState;
}
const dispatch = vi.fn();
await checkForAllExtensionUpdates([extension], dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
payload: {
name: 'error-extension',
state: ExtensionUpdateState.ERROR,
},
);
const result = results.get('error-extension');
expect(result).toBe(ExtensionUpdateState.ERROR);
});
});
});
});