Improved application initialization test. Improved error handling in hunt optimizer.

This commit is contained in:
Daan Vanden Bosch 2019-12-22 02:37:06 +01:00
parent 2083793e67
commit d168030550
13 changed files with 111 additions and 83 deletions

View File

@ -5,5 +5,6 @@ module.exports = {
setupFiles: ["./test/src/setup.js"],
moduleNameMapper: {
"\\.(css|gif|jpg|png|svg|ttf)$": "<rootDir>/src/__mocks__/static_files.js",
"monaco-editor": "<rootDir>/node_modules/monaco-editor/dev/vs/editor/editor.main.js",
},
};

View File

@ -7,6 +7,9 @@ const logger = Logger.get("core/observable/Disposer");
* Container for disposables.
*/
export class Disposer implements Disposable {
private _disposed = false;
private readonly disposables: Disposable[];
/**
* The amount of disposables contained in this disposer.
*/
@ -18,9 +21,6 @@ export class Disposer implements Disposable {
return this._disposed;
}
private _disposed = false;
private readonly disposables: Disposable[];
constructor(...disposables: Disposable[]) {
this.disposables = disposables;
}

View File

@ -12,7 +12,7 @@ import { ItemTypeDto } from "../dto/ItemTypeDto";
import { GuiStore } from "./GuiStore";
import { HttpClient } from "../HttpClient";
export function load_item_type_stores(
export function create_item_type_stores(
http_client: HttpClient,
gui_store: GuiStore,
): ServerMap<ItemTypeStore> {

View File

@ -1,8 +1,6 @@
import { Server } from "../model";
import { Property } from "../observable/property/Property";
import { memoize } from "lodash";
import { sequential } from "../sequential";
import { Disposable } from "../observable/Disposable";
import { GuiStore } from "./GuiStore";
/**
@ -30,9 +28,4 @@ export class ServerMap<T> {
get(server: Server): Promise<T> {
return this.get_value(server);
}
observe_current(f: (current: T) => void, options?: { call_now?: boolean }): Disposable {
const seq_f = sequential(async ({ value }: { value: Promise<T> }) => f(await value));
return this.current.observe(seq_f, options);
}
}

View File

@ -15,6 +15,9 @@ import { SortDirection, Table } from "../../core/gui/Table";
import { list_property } from "../../core/observable";
import { ServerMap } from "../../core/stores/ServerMap";
import { HuntMethodStore } from "../stores/HuntMethodStore";
import Logger from "js-logger";
const logger = Logger.get("hunt_optimizer/gui/MethodsForEpisodeView");
export class MethodsForEpisodeView extends ResizableWidget {
readonly element = el.div({ class: "hunt_optimizer_MethodsForEpisodeView" });
@ -121,8 +124,11 @@ export class MethodsForEpisodeView extends ResizableWidget {
this.element.append(table.element);
this.disposable(
hunt_method_stores.observe_current(
hunt_method_store => {
hunt_method_stores.current.observe(
async ({ value }) => {
try {
const hunt_method_store = await value;
if (this.hunt_methods_observer) {
this.hunt_methods_observer.dispose();
}
@ -137,6 +143,9 @@ export class MethodsForEpisodeView extends ResizableWidget {
call_now: true,
},
);
} catch (e) {
logger.error("Couldn't load hunt optimizer store.", e);
}
},
{ call_now: true },
),

View File

@ -10,6 +10,9 @@ import "./OptimizationResultView.css";
import { Duration } from "luxon";
import { ServerMap } from "../../core/stores/ServerMap";
import { HuntOptimizerStore } from "../stores/HuntOptimizerStore";
import Logger from "js-logger";
const logger = Logger.get("hunt_optimizer/gui/OptimizationResultView");
export class OptimizationResultView extends Widget {
readonly element = el.div(
@ -24,8 +27,11 @@ export class OptimizationResultView extends Widget {
super();
this.disposable(
hunt_optimizer_stores.observe_current(
hunt_optimizer_store => {
hunt_optimizer_stores.current.observe(
async ({ value }) => {
try {
const hunt_optimizer_store = await value;
if (this.results_observer) {
this.results_observer.dispose();
}
@ -36,6 +42,9 @@ export class OptimizationResultView extends Widget {
call_now: true,
},
);
} catch (e) {
logger.error("Couldn't load hunt optimizer store.", e);
}
},
{ call_now: true },
),

View File

@ -11,6 +11,9 @@ import { ItemType } from "../../core/model/items";
import { Disposable } from "../../core/observable/Disposable";
import { ServerMap } from "../../core/stores/ServerMap";
import { HuntOptimizerStore } from "../stores/HuntOptimizerStore";
import Logger from "js-logger";
const logger = Logger.get("hunt_optimizer/gui/WantedItemsView");
export class WantedItemsView extends Widget {
readonly element = el.div({ class: "hunt_optimizer_WantedItemsView" });
@ -48,8 +51,10 @@ export class WantedItemsView extends Widget {
);
this.disposables(
hunt_optimizer_stores.observe_current(
hunt_optimizer_store => {
hunt_optimizer_stores.current.observe(
async ({ value }) => {
try {
const hunt_optimizer_store = await value;
this.store_disposer.dispose_all();
this.store_disposer.add_all(
@ -71,6 +76,9 @@ export class WantedItemsView extends Widget {
.slice()
.sort((a, b) => a.name.localeCompare(b.name));
filtered_huntable_items.val = huntable_items.val;
} catch (e) {
logger.error("Couldn't load hunt optimizer store.", e);
}
},
{ call_now: true },
),

View File

@ -1,8 +1,8 @@
import { HuntOptimizerView } from "./gui/HuntOptimizerView";
import { ServerMap } from "../core/stores/ServerMap";
import { HuntMethodStore, load_hunt_method_stores } from "./stores/HuntMethodStore";
import { HuntMethodStore, create_hunt_method_stores } from "./stores/HuntMethodStore";
import { GuiStore } from "../core/stores/GuiStore";
import { HuntOptimizerStore, load_hunt_optimizer_stores } from "./stores/HuntOptimizerStore";
import { HuntOptimizerStore, create_hunt_optimizer_stores } from "./stores/HuntOptimizerStore";
import { ItemTypeStore } from "../core/stores/ItemTypeStore";
import { HuntMethodPersister } from "./persistence/HuntMethodPersister";
import { HuntOptimizerPersister } from "./persistence/HuntOptimizerPersister";
@ -15,12 +15,12 @@ export function initialize_hunt_optimizer(
item_type_stores: ServerMap<ItemTypeStore>,
item_drop_stores: ServerMap<ItemDropStore>,
): HuntOptimizerView {
const hunt_method_stores: ServerMap<HuntMethodStore> = load_hunt_method_stores(
const hunt_method_stores: ServerMap<HuntMethodStore> = create_hunt_method_stores(
http_client,
gui_store,
new HuntMethodPersister(),
);
const hunt_optimizer_stores: ServerMap<HuntOptimizerStore> = load_hunt_optimizer_stores(
const hunt_optimizer_stores: ServerMap<HuntOptimizerStore> = create_hunt_optimizer_stores(
gui_store,
new HuntOptimizerPersister(item_type_stores),
item_type_stores,

View File

@ -20,7 +20,7 @@ const DEFAULT_DURATION = Duration.fromObject({ minutes: 30 });
const DEFAULT_GOVERNMENT_TEST_DURATION = Duration.fromObject({ minutes: 45 });
const DEFAULT_LARGE_ENEMY_COUNT_DURATION = Duration.fromObject({ minutes: 45 });
export function load_hunt_method_stores(
export function create_hunt_method_stores(
http_client: HttpClient,
gui_store: GuiStore,
hunt_method_persister: HuntMethodPersister,

View File

@ -25,7 +25,7 @@ import { ServerMap } from "../../core/stores/ServerMap";
import { GuiStore } from "../../core/stores/GuiStore";
import { HuntOptimizerPersister } from "../persistence/HuntOptimizerPersister";
export function load_hunt_optimizer_stores(
export function create_hunt_optimizer_stores(
gui_store: GuiStore,
hunt_optimizer_persister: HuntOptimizerPersister,
item_type_stores: ServerMap<ItemTypeStore>,

View File

@ -10,7 +10,7 @@ import { HttpClient } from "../../core/HttpClient";
const logger = Logger.get("stores/ItemDropStore");
export function load_item_drop_stores(
export function create_item_drop_stores(
http_client: HttpClient,
gui_store: GuiStore,
item_type_stores: ServerMap<ItemTypeStore>,

View File

@ -8,15 +8,22 @@ import { initialize } from "./initialize";
import { StubHttpClient } from "./core/HttpClient";
import { DisposableThreeRenderer } from "./core/rendering/Renderer";
test("Initialization and shutdown should succeed without throwing or logging errors.", () => {
for (const path of [undefined, "/viewer", "/quest_editor", "/hunt_optimizer"]) {
const with_path = path == undefined ? "without specific path" : `with path ${path}`;
test(`Initialization and shutdown ${with_path} should succeed without throwing errors or logging with level WARN or above.`, () => {
const logged_errors: string[] = [];
Logger.setHandler((messages: any[], context: IContext) => {
if (context.level.value >= Logger.ERROR.value) {
if (context.level.value >= Logger.WARN.value) {
logged_errors.push(Array.prototype.join.call(messages, " "));
}
});
if (path != undefined) {
window.location.hash = path;
}
const app = initialize(new StubHttpClient(), () => new StubRenderer());
expect(app).toBeDefined();
@ -26,6 +33,7 @@ test("Initialization and shutdown should succeed without throwing or logging err
expect(logged_errors).toEqual([]);
});
}
class StubRenderer implements DisposableThreeRenderer {
domElement: HTMLCanvasElement = document.createElement("canvas");

View File

@ -1,8 +1,8 @@
import { HttpClient } from "./core/HttpClient";
import { Disposable } from "./core/observable/Disposable";
import { GuiStore, GuiTool } from "./core/stores/GuiStore";
import { load_item_type_stores } from "./core/stores/ItemTypeStore";
import { load_item_drop_stores } from "./hunt_optimizer/stores/ItemDropStore";
import { create_item_type_stores } from "./core/stores/ItemTypeStore";
import { create_item_drop_stores } from "./hunt_optimizer/stores/ItemDropStore";
import { ApplicationView } from "./application/gui/ApplicationView";
import { throttle } from "lodash";
import { DisposableThreeRenderer } from "./core/rendering/Renderer";
@ -23,8 +23,8 @@ export function initialize(
// Initialize core stores shared by several submodules.
const gui_store = new GuiStore();
const item_type_stores = load_item_type_stores(http_client, gui_store);
const item_drop_stores = load_item_drop_stores(http_client, gui_store, item_type_stores);
const item_type_stores = create_item_type_stores(http_client, gui_store);
const item_drop_stores = create_item_drop_stores(http_client, gui_store, item_type_stores);
// Initialize application view.
const application_view = new ApplicationView(gui_store, [