Rework the Item code so it can be reused for the history.

This commit is contained in:
Bauke 2023-04-17 16:31:21 +02:00
parent 451ebb8189
commit 091da70bea
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
2 changed files with 33 additions and 21 deletions

View File

@ -1,8 +1,13 @@
import {type TestContext, setup} from "@holllo/test"; import {type TestContext, setup} from "@holllo/test";
import {type Value} from "@holllo/webextension-storage"; import {type Value} from "@holllo/webextension-storage";
import browser from "webextension-polyfill";
import {type Item, createItem, nextItem, nextItemId, storage} from "./item.js"; import {
type Item,
createItem,
nextItem,
nextItemId,
storageForPrefix,
} from "./item.js";
const testText = "Test Item"; const testText = "Test Item";
const testUrl = "https://example.org/"; const testUrl = "https://example.org/";
@ -30,10 +35,8 @@ await setup(
"Item", "Item",
async (group) => { async (group) => {
group.beforeAll(async () => { group.beforeAll(async () => {
// If we're in production and testing, clear item storage. // TODO: Temporarily store existing storage and run the tests, and then
if (!$dev && $test) { // restore it again.
await storage.clear();
}
}); });
group.test("create & nextItem", async (test) => { group.test("create & nextItem", async (test) => {
@ -67,11 +70,7 @@ await setup(
await Promise.all(items.map(async (item) => item.remove())); await Promise.all(items.map(async (item) => item.remove()));
// After all items have been removed test that `nextItem` returns nothing. // After all items have been removed test that `nextItem` returns nothing.
// This test will fail if an item is left from development, to clear // This test will fail if an item is left from development.
// storage automatically run in production and have test enabled.
// ie. `NODE_ENV=production TEST=true makers dev`
// TODO: Temporarily store existing storage and run the tests, and then
// restore it again.
storedNext = await nextItem(); storedNext = await nextItem();
test.equals(storedNext, undefined, "next item is undefined"); test.equals(storedNext, undefined, "next item is undefined");
}); });

View File

@ -29,10 +29,13 @@ export type SerializedItem = {
}; };
/** The key prefix for {@link Item}s. */ /** The key prefix for {@link Item}s. */
export const itemKeyPrefix = "item-"; export type ItemKeyPrefix = "item-" | "history-";
/** The default storage area to use for {@link Item}s. */ export function storageForPrefix(
export const storage = browser.storage.sync; prefix: ItemKeyPrefix,
): browser.Storage.StorageArea {
return prefix === "item-" ? browser.storage.sync : browser.storage.local;
}
/** /**
* Serialize and JSON-stringify an {@link Item}. * Serialize and JSON-stringify an {@link Item}.
@ -83,18 +86,20 @@ export const deserializeItem: Value<Item>["deserialize"] = (
* *
* @param text The text of the {@link Item} to create. * @param text The text of the {@link Item} to create.
* @param url The URL of the {@link Item} to create. * @param url The URL of the {@link Item} to create.
* @param itemKeyPrefix The prefix for the {@link Item} key.
* @returns The created {@link Value} with inner {@link Item}. * @returns The created {@link Value} with inner {@link Item}.
*/ */
export async function createItem( export async function createItem(
text: Item["text"], text: Item["text"],
url: Item["url"], url: Item["url"],
itemKeyPrefix: ItemKeyPrefix = "item-",
): Promise<Value<Item>> { ): Promise<Value<Item>> {
const nextId = await nextItemId(); const nextId = await nextItemId();
const item = await createValue<Item>({ const item = await createValue<Item>({
deserialize: deserializeItem, deserialize: deserializeItem,
serialize: serializeItem, serialize: serializeItem,
storage, storage: storageForPrefix(itemKeyPrefix),
key: `${itemKeyPrefix}${nextId}`, key: `${itemKeyPrefix}${nextId}`,
value: { value: {
dateAdded: new Date(), dateAdded: new Date(),
@ -108,11 +113,14 @@ export async function createItem(
} }
/** /**
* Get all keys from storage that start with the {@link itemKeyPrefix}. * Get all keys from storage that start with the {@link ItemKeyPrefix}.
* *
* @returns The keys as a string array. * @returns The keys as a string array.
*/ */
export async function getItemKeys(): Promise<string[]> { export async function getItemKeys(
itemKeyPrefix: ItemKeyPrefix,
): Promise<string[]> {
const storage = storageForPrefix(itemKeyPrefix);
const stored = Object.keys(await storage.get()); const stored = Object.keys(await storage.get());
const keys = stored.filter((key) => key.startsWith(itemKeyPrefix)); const keys = stored.filter((key) => key.startsWith(itemKeyPrefix));
return keys; return keys;
@ -123,9 +131,11 @@ export async function getItemKeys(): Promise<string[]> {
* *
* @returns The next ID as a number. * @returns The next ID as a number.
*/ */
export async function nextItemId(): Promise<number> { export async function nextItemId(
itemKeyPrefix: ItemKeyPrefix = "item-",
): Promise<number> {
// Get all the item keys and sort them so the highest ID is first. // Get all the item keys and sort them so the highest ID is first.
const keys = await getItemKeys(); const keys = await getItemKeys(itemKeyPrefix);
keys.sort((a, b) => b.localeCompare(a)); keys.sort((a, b) => b.localeCompare(a));
// Get the first key or use 0 if no items exist yet. // Get the first key or use 0 if no items exist yet.
@ -142,8 +152,10 @@ export async function nextItemId(): Promise<number> {
* queue is empty. * queue is empty.
*/ */
export async function nextItem(): Promise<Value<Item> | undefined> { export async function nextItem(): Promise<Value<Item> | undefined> {
const itemKeyPrefix: ItemKeyPrefix = "item-";
// Get all the item keys and sort them so the lowest ID is first. // Get all the item keys and sort them so the lowest ID is first.
const keys = await getItemKeys(); const keys = await getItemKeys(itemKeyPrefix);
keys.sort((a, b) => a.localeCompare(b)); keys.sort((a, b) => a.localeCompare(b));
// If no keys exist then exit early. // If no keys exist then exit early.
@ -166,7 +178,8 @@ export async function nextItem(): Promise<Value<Item> | undefined> {
* Set the WebExtension's badge text to show the current {@link Item} count. * Set the WebExtension's badge text to show the current {@link Item} count.
*/ */
export async function setBadgeText(): Promise<void> { export async function setBadgeText(): Promise<void> {
const keys = await getItemKeys(); const itemKeyPrefix: ItemKeyPrefix = "item-";
const keys = await getItemKeys(itemKeyPrefix);
const count = keys.length; const count = keys.length;
const action: browser.Action.Static = const action: browser.Action.Static =
$browser === "firefox" ? browser.browserAction : browser.action; $browser === "firefox" ? browser.browserAction : browser.action;