[PM-10788] Handle `chrome.runtime.lastError` (#10479)

* Handle `chrome.runtime.lastError`

* Add Tests
This commit is contained in:
Justin Baur 2024-08-12 13:13:34 -04:00 committed by GitHub
parent 7387a1115a
commit 334601e74f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 7 deletions

View File

@ -74,8 +74,12 @@ export default abstract class AbstractChromeStorageService
} }
async get<T>(key: string): Promise<T> { async get<T>(key: string): Promise<T> {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
this.chromeStorageApi.get(key, (obj: any) => { this.chromeStorageApi.get(key, (obj) => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
if (obj != null && obj[key] != null) { if (obj != null && obj[key] != null) {
resolve(this.processGetObject(obj[key])); resolve(this.processGetObject(obj[key]));
return; return;
@ -98,16 +102,24 @@ export default abstract class AbstractChromeStorageService
} }
const keyedObj = { [key]: obj }; const keyedObj = { [key]: obj };
return new Promise<void>((resolve) => { return new Promise<void>((resolve, reject) => {
this.chromeStorageApi.set(keyedObj, () => { this.chromeStorageApi.set(keyedObj, () => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(); resolve();
}); });
}); });
} }
async remove(key: string): Promise<void> { async remove(key: string): Promise<void> {
return new Promise<void>((resolve) => { return new Promise<void>((resolve, reject) => {
this.chromeStorageApi.remove(key, () => { this.chromeStorageApi.remove(key, () => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(); resolve();
}); });
}); });

View File

@ -51,6 +51,10 @@ describe("ChromeStorageApiService", () => {
}); });
}); });
afterEach(() => {
chrome.runtime.lastError = undefined;
});
it("uses `objToStore` to prepare a value for set", async () => { it("uses `objToStore` to prepare a value for set", async () => {
const key = "key"; const key = "key";
const value = { key: "value" }; const value = { key: "value" };
@ -73,6 +77,15 @@ describe("ChromeStorageApiService", () => {
await service.save(key, null); await service.save(key, null);
expect(removeMock).toHaveBeenCalledWith(key, expect.any(Function)); expect(removeMock).toHaveBeenCalledWith(key, expect.any(Function));
}); });
it("translates chrome.runtime.lastError to promise rejection", async () => {
setMock.mockImplementation((data, callback) => {
chrome.runtime.lastError = new Error("Test Error");
callback();
});
await expect(async () => await service.save("test", {})).rejects.toThrow("Test Error");
});
}); });
describe("get", () => { describe("get", () => {
@ -87,6 +100,10 @@ describe("ChromeStorageApiService", () => {
}); });
}); });
afterEach(() => {
chrome.runtime.lastError = undefined;
});
it("returns a stored value when it is serialized", async () => { it("returns a stored value when it is serialized", async () => {
const value = { key: "value" }; const value = { key: "value" };
store[key] = objToStore(value); store[key] = objToStore(value);
@ -112,5 +129,15 @@ describe("ChromeStorageApiService", () => {
const result = await service.get(key); const result = await service.get(key);
expect(result).toBeNull(); expect(result).toBeNull();
}); });
it("translates chrome.runtime.lastError to promise rejection", async () => {
getMock.mockImplementation((key, callback) => {
chrome.runtime.lastError = new Error("Test Error");
callback();
chrome.runtime.lastError = undefined;
});
await expect(async () => await service.get("test")).rejects.toThrow("Test Error");
});
}); });
}); });

View File

@ -62,6 +62,7 @@ describe("BrowserLocalStorageService", () => {
}); });
afterEach(() => { afterEach(() => {
chrome.runtime.lastError = undefined;
jest.resetAllMocks(); jest.resetAllMocks();
}); });
@ -121,6 +122,24 @@ describe("BrowserLocalStorageService", () => {
expect(clearMock).toHaveBeenCalledTimes(1); expect(clearMock).toHaveBeenCalledTimes(1);
}); });
it("throws if get has chrome.runtime.lastError", async () => {
getMock.mockImplementation((key, callback) => {
chrome.runtime.lastError = new Error("Get Test Error");
callback();
});
await expect(async () => await service.reseed()).rejects.toThrow("Get Test Error");
});
it("throws if save has chrome.runtime.lastError", async () => {
saveMock.mockImplementation((obj, callback) => {
chrome.runtime.lastError = new Error("Save Test Error");
callback();
});
await expect(async () => await service.reseed()).rejects.toThrow("Save Test Error");
});
}); });
describe.each(["get", "has", "save", "remove"] as const)("%s", (method) => { describe.each(["get", "has", "save", "remove"] as const)("%s", (method) => {

View File

@ -81,8 +81,11 @@ export default class BrowserLocalStorageService extends AbstractChromeStorageSer
* Clears local storage * Clears local storage
*/ */
private async clear() { private async clear() {
return new Promise<void>((resolve) => { return new Promise<void>((resolve, reject) => {
this.chromeStorageApi.clear(() => { this.chromeStorageApi.clear(() => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(); resolve();
}); });
}); });
@ -95,8 +98,12 @@ export default class BrowserLocalStorageService extends AbstractChromeStorageSer
* @returns Promise resolving to keyed object of all stored data * @returns Promise resolving to keyed object of all stored data
*/ */
private async getAll(): Promise<Record<string, unknown>> { private async getAll(): Promise<Record<string, unknown>> {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
this.chromeStorageApi.get(null, (allStorage) => { this.chromeStorageApi.get(null, (allStorage) => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
const resolved = Object.entries(allStorage).reduce( const resolved = Object.entries(allStorage).reduce(
(agg, [key, value]) => { (agg, [key, value]) => {
agg[key] = this.processGetObject(value); agg[key] = this.processGetObject(value);
@ -110,7 +117,7 @@ export default class BrowserLocalStorageService extends AbstractChromeStorageSer
} }
private async saveAll(data: Record<string, unknown>): Promise<void> { private async saveAll(data: Record<string, unknown>): Promise<void> {
return new Promise<void>((resolve) => { return new Promise<void>((resolve, reject) => {
const keyedData = Object.entries(data).reduce( const keyedData = Object.entries(data).reduce(
(agg, [key, value]) => { (agg, [key, value]) => {
agg[key] = objToStore(value); agg[key] = objToStore(value);
@ -119,6 +126,10 @@ export default class BrowserLocalStorageService extends AbstractChromeStorageSer
{} as Record<string, SerializedValue>, {} as Record<string, SerializedValue>,
); );
this.chromeStorageApi.set(keyedData, () => { this.chromeStorageApi.set(keyedData, () => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(); resolve();
}); });
}); });