From d168030550b1d94a58836be8b6faf49cfbae4472 Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Sun, 22 Dec 2019 02:37:06 +0100 Subject: [PATCH] Improved application initialization test. Improved error handling in hunt optimizer. --- jest.config.js | 1 + src/core/observable/Disposer.ts | 6 +-- src/core/stores/ItemTypeStore.ts | 2 +- src/core/stores/ServerMap.ts | 7 --- .../gui/MethodsForEpisodeView.ts | 39 +++++++++------ .../gui/OptimizationResultView.ts | 31 +++++++----- src/hunt_optimizer/gui/WantedItemsView.ts | 48 +++++++++++-------- src/hunt_optimizer/index.ts | 8 ++-- src/hunt_optimizer/stores/HuntMethodStore.ts | 2 +- .../stores/HuntOptimizerStore.ts | 2 +- src/hunt_optimizer/stores/ItemDropStore.ts | 2 +- src/initialize.test.ts | 38 +++++++++------ src/initialize.ts | 8 ++-- 13 files changed, 111 insertions(+), 83 deletions(-) diff --git a/jest.config.js b/jest.config.js index b31838c4..62cdd3b5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,5 +5,6 @@ module.exports = { setupFiles: ["./test/src/setup.js"], moduleNameMapper: { "\\.(css|gif|jpg|png|svg|ttf)$": "/src/__mocks__/static_files.js", + "monaco-editor": "/node_modules/monaco-editor/dev/vs/editor/editor.main.js", }, }; diff --git a/src/core/observable/Disposer.ts b/src/core/observable/Disposer.ts index beb6d3f3..e5577d1a 100644 --- a/src/core/observable/Disposer.ts +++ b/src/core/observable/Disposer.ts @@ -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; } diff --git a/src/core/stores/ItemTypeStore.ts b/src/core/stores/ItemTypeStore.ts index dc4abc9b..4a826f93 100644 --- a/src/core/stores/ItemTypeStore.ts +++ b/src/core/stores/ItemTypeStore.ts @@ -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 { diff --git a/src/core/stores/ServerMap.ts b/src/core/stores/ServerMap.ts index 02f142fe..a25f36e0 100644 --- a/src/core/stores/ServerMap.ts +++ b/src/core/stores/ServerMap.ts @@ -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 { get(server: Server): Promise { return this.get_value(server); } - - observe_current(f: (current: T) => void, options?: { call_now?: boolean }): Disposable { - const seq_f = sequential(async ({ value }: { value: Promise }) => f(await value)); - return this.current.observe(seq_f, options); - } } diff --git a/src/hunt_optimizer/gui/MethodsForEpisodeView.ts b/src/hunt_optimizer/gui/MethodsForEpisodeView.ts index e225cc1e..013f087a 100644 --- a/src/hunt_optimizer/gui/MethodsForEpisodeView.ts +++ b/src/hunt_optimizer/gui/MethodsForEpisodeView.ts @@ -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,22 +124,28 @@ export class MethodsForEpisodeView extends ResizableWidget { this.element.append(table.element); this.disposable( - hunt_method_stores.observe_current( - hunt_method_store => { - if (this.hunt_methods_observer) { - this.hunt_methods_observer.dispose(); - } + hunt_method_stores.current.observe( + async ({ value }) => { + try { + const hunt_method_store = await value; - this.hunt_methods_observer = hunt_method_store.methods.observe( - ({ value }) => { - hunt_methods.val = value.filter( - method => method.episode === this.episode, - ); - }, - { - call_now: true, - }, - ); + if (this.hunt_methods_observer) { + this.hunt_methods_observer.dispose(); + } + + this.hunt_methods_observer = hunt_method_store.methods.observe( + ({ value }) => { + hunt_methods.val = value.filter( + method => method.episode === this.episode, + ); + }, + { + call_now: true, + }, + ); + } catch (e) { + logger.error("Couldn't load hunt optimizer store.", e); + } }, { call_now: true }, ), diff --git a/src/hunt_optimizer/gui/OptimizationResultView.ts b/src/hunt_optimizer/gui/OptimizationResultView.ts index 3db42f05..d2db1bb6 100644 --- a/src/hunt_optimizer/gui/OptimizationResultView.ts +++ b/src/hunt_optimizer/gui/OptimizationResultView.ts @@ -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,18 +27,24 @@ export class OptimizationResultView extends Widget { super(); this.disposable( - hunt_optimizer_stores.observe_current( - hunt_optimizer_store => { - if (this.results_observer) { - this.results_observer.dispose(); - } + hunt_optimizer_stores.current.observe( + async ({ value }) => { + try { + const hunt_optimizer_store = await value; - this.results_observer = hunt_optimizer_store.result.observe( - ({ value }) => this.update_table(value), - { - call_now: true, - }, - ); + if (this.results_observer) { + this.results_observer.dispose(); + } + + this.results_observer = hunt_optimizer_store.result.observe( + ({ value }) => this.update_table(value), + { + call_now: true, + }, + ); + } catch (e) { + logger.error("Couldn't load hunt optimizer store.", e); + } }, { call_now: true }, ), diff --git a/src/hunt_optimizer/gui/WantedItemsView.ts b/src/hunt_optimizer/gui/WantedItemsView.ts index 111dfab4..d6794ded 100644 --- a/src/hunt_optimizer/gui/WantedItemsView.ts +++ b/src/hunt_optimizer/gui/WantedItemsView.ts @@ -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,29 +51,34 @@ export class WantedItemsView extends Widget { ); this.disposables( - hunt_optimizer_stores.observe_current( - hunt_optimizer_store => { - this.store_disposer.dispose_all(); + hunt_optimizer_stores.current.observe( + async ({ value }) => { + try { + const hunt_optimizer_store = await value; + this.store_disposer.dispose_all(); - this.store_disposer.add_all( - bind_children_to( - this.tbody_element, - hunt_optimizer_store.wanted_items, - this.create_row, - ), + this.store_disposer.add_all( + bind_children_to( + this.tbody_element, + hunt_optimizer_store.wanted_items, + this.create_row, + ), - combo_box.selected.observe(({ value: item_type }) => { - if (item_type) { - hunt_optimizer_store.add_wanted_item(item_type); - combo_box.selected.val = undefined; - } - }), - ); + combo_box.selected.observe(({ value: item_type }) => { + if (item_type) { + hunt_optimizer_store.add_wanted_item(item_type); + combo_box.selected.val = undefined; + } + }), + ); - huntable_items.val = hunt_optimizer_store.huntable_item_types - .slice() - .sort((a, b) => a.name.localeCompare(b.name)); - filtered_huntable_items.val = huntable_items.val; + huntable_items.val = hunt_optimizer_store.huntable_item_types + .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 }, ), diff --git a/src/hunt_optimizer/index.ts b/src/hunt_optimizer/index.ts index 94fc60dd..c8b2a593 100644 --- a/src/hunt_optimizer/index.ts +++ b/src/hunt_optimizer/index.ts @@ -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, item_drop_stores: ServerMap, ): HuntOptimizerView { - const hunt_method_stores: ServerMap = load_hunt_method_stores( + const hunt_method_stores: ServerMap = create_hunt_method_stores( http_client, gui_store, new HuntMethodPersister(), ); - const hunt_optimizer_stores: ServerMap = load_hunt_optimizer_stores( + const hunt_optimizer_stores: ServerMap = create_hunt_optimizer_stores( gui_store, new HuntOptimizerPersister(item_type_stores), item_type_stores, diff --git a/src/hunt_optimizer/stores/HuntMethodStore.ts b/src/hunt_optimizer/stores/HuntMethodStore.ts index adf178c6..ea318339 100644 --- a/src/hunt_optimizer/stores/HuntMethodStore.ts +++ b/src/hunt_optimizer/stores/HuntMethodStore.ts @@ -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, diff --git a/src/hunt_optimizer/stores/HuntOptimizerStore.ts b/src/hunt_optimizer/stores/HuntOptimizerStore.ts index c19f1c17..b5308356 100644 --- a/src/hunt_optimizer/stores/HuntOptimizerStore.ts +++ b/src/hunt_optimizer/stores/HuntOptimizerStore.ts @@ -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, diff --git a/src/hunt_optimizer/stores/ItemDropStore.ts b/src/hunt_optimizer/stores/ItemDropStore.ts index 960c71e6..cb389508 100644 --- a/src/hunt_optimizer/stores/ItemDropStore.ts +++ b/src/hunt_optimizer/stores/ItemDropStore.ts @@ -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, diff --git a/src/initialize.test.ts b/src/initialize.test.ts index fc147818..7dc2025b 100644 --- a/src/initialize.test.ts +++ b/src/initialize.test.ts @@ -8,24 +8,32 @@ 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.", () => { - const logged_errors: string[] = []; +for (const path of [undefined, "/viewer", "/quest_editor", "/hunt_optimizer"]) { + const with_path = path == undefined ? "without specific path" : `with path ${path}`; - Logger.setHandler((messages: any[], context: IContext) => { - if (context.level.value >= Logger.ERROR.value) { - logged_errors.push(Array.prototype.join.call(messages, " ")); + 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.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(); + expect(logged_errors).toEqual([]); + + app.dispose(); + + expect(logged_errors).toEqual([]); }); - - const app = initialize(new StubHttpClient(), () => new StubRenderer()); - - expect(app).toBeDefined(); - expect(logged_errors).toEqual([]); - - app.dispose(); - - expect(logged_errors).toEqual([]); -}); +} class StubRenderer implements DisposableThreeRenderer { domElement: HTMLCanvasElement = document.createElement("canvas"); diff --git a/src/initialize.ts b/src/initialize.ts index 92eff1fc..1f8bdee9 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -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, [