Add WidgetManager tests

master
rubenwardy 2021-06-05 16:46:24 +01:00
parent bdb2bb294d
commit f23418020a
14 changed files with 203 additions and 33 deletions

View File

@ -17,7 +17,7 @@
"package:chrome": "npm run build:app && web-ext build -s ./dist/webext/ -i app/manifest.json -i app/index.html -i manifest.firefox.json -n chrome.zip",
"package:firefox": "npm run build:app && cp src/webext/manifest.firefox.json dist/webext/manifest.json && web-ext build -s ./dist/webext/ -i app/manifest.json -i manifest.firefox.json -i app/index.html -n firefox.zip",
"lint": "eslint 'src/**/*.{js,ts,tsx}' --fix",
"test": "NODE_PATH=src mocha -r ts-node/register tests/**/*.test.ts",
"test": "TS_NODE_FILES=1 NODE_PATH=src mocha -r ts-node/register tests/**/*.test.ts",
"coverage": "nyc -r lcov -e .ts -x \"*.test.ts\" npm run test",
"trans:extract": "formatjs extract 'src/app/**/*.ts*' --out-file src/app/locale/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format utils/translation-format.js && node utils/update_translations.js",
"trans:compile": "node utils/compile_translations.js"

View File

@ -119,7 +119,7 @@ class DelegateStorage implements IStorage {
}
if (typeof browser === 'undefined' &&
if (typeof browser === 'undefined' && typeof navigator !== "undefined" &&
navigator.storage && navigator.storage.persist) {
navigator.storage.persist()
.then((isPersisted) =>

View File

@ -1,4 +1,4 @@
import { storage } from "./Storage";
import { IStorage } from "./Storage";
import { Vector2 } from "./utils/Vector2";
import { WidgetTypes } from "./widgets";
import { getInitialTheme, Widget } from "./Widget";
@ -13,10 +13,10 @@ export class WidgetManager {
widgets: (Widget<any>)[] = [];
constructor() {}
constructor(private storage: IStorage) {}
async load() {
const json = await storage.get<Widget<any>[]>("widgets");
const json = await this.storage.get<Widget<any>[]>("widgets");
if (!json) {
this.resetToDefault();
return;
@ -43,7 +43,7 @@ export class WidgetManager {
}
save() {
storage.set("widgets", this.widgets);
this.storage.set("widgets", this.widgets);
}
resetToDefault() {
@ -52,11 +52,11 @@ export class WidgetManager {
"HelpAbout", "Weather", "Feed", "Notes"].forEach(this.createWidget.bind(this));
}
createWidget(type: string) {
createWidget<T>(type: string): Widget<T> {
this.id_counter++;
const widget_type = WidgetTypes[type];
const widget = {
const widget: Widget<T> = {
id: this.id_counter,
type: type,
position: undefined,
@ -71,6 +71,8 @@ export class WidgetManager {
}
this.save();
return widget;
}
removeWidget(id: number) {

View File

@ -10,9 +10,10 @@ import { FormattedMessage, IntlProvider } from "react-intl";
import { getTranslation, getUserLocale } from "app/locale";
import { applyTheme, ThemeConfig } from "./settings/ThemeSettings";
import ReviewRequester from "./ReviewRequester";
import { storage } from "app/Storage";
const widgetManager = new WidgetManager();
const widgetManager = new WidgetManager(storage);
export default function App() {
const [loadingRes,] = usePromise(async () => {

7
src/app/custom.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
interface Config {
API_URL: string;
PROXY_URL: string;
}
declare var config: Config;
declare const app_version: string;

View File

@ -3,14 +3,13 @@ import { miscMessages } from "app/locale/common";
import { IntlShape, useIntl } from "react-intl";
import { usePromise } from "./promises";
const proxy_url = config.PROXY_URL;
function makeProxy(url: string) {
if (typeof browser !== 'undefined') {
console.log("Detected running as webext");
return url;
} else {
const ret = new URL(proxy_url);
const ret = new URL(config.PROXY_URL);
ret.searchParams.set("url", url);
return ret.toString();
}

View File

@ -1,14 +1,3 @@
interface Config {
API_URL: string;
PROXY_URL: string;
PROXY_ALLOWED_HOSTS: string[];
}
declare global {
const config: Config;
const app_version: string;
}
import React from "react";
import { render } from "react-dom";
import App from "./components/App";

25
tests/app/DummyStorage.ts Normal file
View File

@ -0,0 +1,25 @@
import { IStorage } from "app/Storage";
export default class DummyStorage implements IStorage {
private values: { [key: string]: any } = {};
async getAll(): Promise<{ [key: string]: any; }> {
return { ...this.values };
}
async get<T>(key: string): Promise<T | null> {
return this.values[key] ?? null;
}
async set<T>(key: string, value: T): Promise<void> {
this.values[key] = value;
}
async remove(key: string): Promise<void> {
delete this.values[key];
}
async clear(): Promise<void> {
this.values = {}
}
}

View File

@ -36,7 +36,10 @@ describe("WidgetLayouter", function() {
type: "Type",
position: V(1, 1),
size: V(3, 3),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
layouter.resolveAll([widget]);
@ -52,21 +55,30 @@ describe("WidgetLayouter", function() {
id: 1,
type: "Type",
size: V(3, 3),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
const widget2 : Widget<any> = {
id: 1,
type: "Type",
size: V(15, 2),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
const widget3 : Widget<any> = {
id: 1,
type: "Type",
size: V(3, 3),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
layouter.resolveAll([widget1, widget2, widget3]);
@ -86,7 +98,10 @@ describe("WidgetLayouter", function() {
id: 1,
type: "Type",
size: V(3, 3),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
const widget2 : Widget<any> = {
@ -94,7 +109,10 @@ describe("WidgetLayouter", function() {
type: "Type",
position: V(0, 0),
size: V(15, 2),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
const widget3 : Widget<any> = {
@ -102,7 +120,10 @@ describe("WidgetLayouter", function() {
type: "Type",
position: V(3, 0),
size: V(3, 3),
props: {}
props: {},
theme: {
showPanelBG: false
}
};
layouter.resolveAll([widget1, widget2, widget3]);

View File

@ -0,0 +1,123 @@
import { LinkBoxProps } from "app/components/LinkBox";
import { Vector2 } from "app/utils/Vector2";
import { WidgetManager } from "app/WidgetManager";
import { expect } from "chai";
import DummyStorage from "./DummyStorage";
describe("WidgetManager::create", () => {
it("createsDefaultWidgets", async () => {
const storage = new DummyStorage();
const wm = new WidgetManager(storage);
await wm.load();
expect(wm.widgets.length).to.equal(9);
});
it("copiesProps", async () => {
const storage = new DummyStorage();
storage.set("widgets", [
{
id: "123",
type: "Notes",
props: {},
size: new Vector2(3, 5),
},
]);
const wm = new WidgetManager(storage);
expect(wm.widgets.length).to.equal(0);
await wm.load();
expect(wm.widgets.length).to.equal(1);
const widget1 = wm.createWidget<LinkBoxProps>("Links");
const widget2 = wm.createWidget<LinkBoxProps>("Links");
expect(widget1.props.links.length).to.equal(6);
expect(widget2.props.links.length).to.equal(6);
widget1.props.links.push({
id: "123",
title: "Title",
url: "url",
});
expect(widget1.props.links.length).to.equal(7);
expect(widget2.props.links.length).to.equal(6);
});
it("copiesTheme", async () => {
const storage = new DummyStorage();
storage.set("widgets", []);
const wm = new WidgetManager(storage);
await wm.load();
expect(wm.widgets.length).to.equal(0);
const widget1 = wm.createWidget<LinkBoxProps>("Links");
const widget2 = wm.createWidget<LinkBoxProps>("Links");
expect(widget1.theme.useIconBar).is.false;
widget1.theme.useIconBar = true;
expect(widget1.theme.useIconBar).is.true;
expect(widget2.theme.useIconBar).is.false;
});
});
describe("WidgetManager::load", () => {
it("adds theme", async () => {
const storage = new DummyStorage();
storage.set("widgets", [
{
id: "123",
type: "Notes",
props: {},
size: new Vector2(3, 5),
},
{
id: "124",
type: "Clock",
props: {},
size: new Vector2(3, 5),
},
]);
const wm = new WidgetManager(storage);
expect(wm.widgets.length).to.equal(0);
await wm.load();
expect(wm.widgets.length).to.equal(2);
{
const notesWidget = wm.widgets[0];
expect(notesWidget.theme).not.null;
expect(notesWidget.theme.showPanelBG).to.be.true;
}
{
const clockWidget = wm.widgets[1];
expect(clockWidget.theme).not.null;
expect(clockWidget.theme.showPanelBG).to.be.false;
}
});
it("runs load hook", async () => {
const storage = new DummyStorage();
storage.set("widgets", [
{
id: "123",
type: "TopSites",
props: {
useIconBar: false,
},
size: new Vector2(3, 5),
},
]);
const wm = new WidgetManager(storage);
expect(wm.widgets.length).to.equal(0);
await wm.load();
expect(wm.widgets.length).to.equal(1);
const widget = wm.widgets[0];
expect(widget.props).not.to.haveOwnProperty("useIconBar");
expect(widget.theme).not.null;
expect(widget.theme.showPanelBG).to.be.true;
expect(widget.theme.useIconBar).to.be.false;
});
});

View File

@ -4,6 +4,7 @@
"module": "ES2015"
},
"exclude": [
"src/server"
"src/server",
"tests"
]
}

View File

@ -7,7 +7,8 @@
"sourceMap": true,
"typeRoots": [
"./node_modules/@types",
"./node_modules/web-ext-types"
"./node_modules/web-ext-types",
"src/app/custom.d.ts"
],
"baseUrl": "src",
"esModuleInterop": true,
@ -15,6 +16,6 @@
"resolveJsonModule": true
},
"include": [
"src"
"src", "tests"
]
}

View File

@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"exclude": [
"src/app",
"src/webext"
"src/webext",
"tests"
]
}
//