mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
Improved application initialization test. Improved error handling in hunt optimizer.
This commit is contained in:
parent
2083793e67
commit
d168030550
@ -5,5 +5,6 @@ module.exports = {
|
|||||||
setupFiles: ["./test/src/setup.js"],
|
setupFiles: ["./test/src/setup.js"],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
"\\.(css|gif|jpg|png|svg|ttf)$": "<rootDir>/src/__mocks__/static_files.js",
|
"\\.(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",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,9 @@ const logger = Logger.get("core/observable/Disposer");
|
|||||||
* Container for disposables.
|
* Container for disposables.
|
||||||
*/
|
*/
|
||||||
export class Disposer implements Disposable {
|
export class Disposer implements Disposable {
|
||||||
|
private _disposed = false;
|
||||||
|
private readonly disposables: Disposable[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of disposables contained in this disposer.
|
* The amount of disposables contained in this disposer.
|
||||||
*/
|
*/
|
||||||
@ -18,9 +21,6 @@ export class Disposer implements Disposable {
|
|||||||
return this._disposed;
|
return this._disposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _disposed = false;
|
|
||||||
private readonly disposables: Disposable[];
|
|
||||||
|
|
||||||
constructor(...disposables: Disposable[]) {
|
constructor(...disposables: Disposable[]) {
|
||||||
this.disposables = disposables;
|
this.disposables = disposables;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import { ItemTypeDto } from "../dto/ItemTypeDto";
|
|||||||
import { GuiStore } from "./GuiStore";
|
import { GuiStore } from "./GuiStore";
|
||||||
import { HttpClient } from "../HttpClient";
|
import { HttpClient } from "../HttpClient";
|
||||||
|
|
||||||
export function load_item_type_stores(
|
export function create_item_type_stores(
|
||||||
http_client: HttpClient,
|
http_client: HttpClient,
|
||||||
gui_store: GuiStore,
|
gui_store: GuiStore,
|
||||||
): ServerMap<ItemTypeStore> {
|
): ServerMap<ItemTypeStore> {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { Server } from "../model";
|
import { Server } from "../model";
|
||||||
import { Property } from "../observable/property/Property";
|
import { Property } from "../observable/property/Property";
|
||||||
import { memoize } from "lodash";
|
import { memoize } from "lodash";
|
||||||
import { sequential } from "../sequential";
|
|
||||||
import { Disposable } from "../observable/Disposable";
|
|
||||||
import { GuiStore } from "./GuiStore";
|
import { GuiStore } from "./GuiStore";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,9 +28,4 @@ export class ServerMap<T> {
|
|||||||
get(server: Server): Promise<T> {
|
get(server: Server): Promise<T> {
|
||||||
return this.get_value(server);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,9 @@ import { SortDirection, Table } from "../../core/gui/Table";
|
|||||||
import { list_property } from "../../core/observable";
|
import { list_property } from "../../core/observable";
|
||||||
import { ServerMap } from "../../core/stores/ServerMap";
|
import { ServerMap } from "../../core/stores/ServerMap";
|
||||||
import { HuntMethodStore } from "../stores/HuntMethodStore";
|
import { HuntMethodStore } from "../stores/HuntMethodStore";
|
||||||
|
import Logger from "js-logger";
|
||||||
|
|
||||||
|
const logger = Logger.get("hunt_optimizer/gui/MethodsForEpisodeView");
|
||||||
|
|
||||||
export class MethodsForEpisodeView extends ResizableWidget {
|
export class MethodsForEpisodeView extends ResizableWidget {
|
||||||
readonly element = el.div({ class: "hunt_optimizer_MethodsForEpisodeView" });
|
readonly element = el.div({ class: "hunt_optimizer_MethodsForEpisodeView" });
|
||||||
@ -121,22 +124,28 @@ export class MethodsForEpisodeView extends ResizableWidget {
|
|||||||
this.element.append(table.element);
|
this.element.append(table.element);
|
||||||
|
|
||||||
this.disposable(
|
this.disposable(
|
||||||
hunt_method_stores.observe_current(
|
hunt_method_stores.current.observe(
|
||||||
hunt_method_store => {
|
async ({ value }) => {
|
||||||
if (this.hunt_methods_observer) {
|
try {
|
||||||
this.hunt_methods_observer.dispose();
|
const hunt_method_store = await value;
|
||||||
}
|
|
||||||
|
|
||||||
this.hunt_methods_observer = hunt_method_store.methods.observe(
|
if (this.hunt_methods_observer) {
|
||||||
({ value }) => {
|
this.hunt_methods_observer.dispose();
|
||||||
hunt_methods.val = value.filter(
|
}
|
||||||
method => method.episode === this.episode,
|
|
||||||
);
|
this.hunt_methods_observer = hunt_method_store.methods.observe(
|
||||||
},
|
({ value }) => {
|
||||||
{
|
hunt_methods.val = value.filter(
|
||||||
call_now: true,
|
method => method.episode === this.episode,
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
|
{
|
||||||
|
call_now: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Couldn't load hunt optimizer store.", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ call_now: true },
|
{ call_now: true },
|
||||||
),
|
),
|
||||||
|
@ -10,6 +10,9 @@ import "./OptimizationResultView.css";
|
|||||||
import { Duration } from "luxon";
|
import { Duration } from "luxon";
|
||||||
import { ServerMap } from "../../core/stores/ServerMap";
|
import { ServerMap } from "../../core/stores/ServerMap";
|
||||||
import { HuntOptimizerStore } from "../stores/HuntOptimizerStore";
|
import { HuntOptimizerStore } from "../stores/HuntOptimizerStore";
|
||||||
|
import Logger from "js-logger";
|
||||||
|
|
||||||
|
const logger = Logger.get("hunt_optimizer/gui/OptimizationResultView");
|
||||||
|
|
||||||
export class OptimizationResultView extends Widget {
|
export class OptimizationResultView extends Widget {
|
||||||
readonly element = el.div(
|
readonly element = el.div(
|
||||||
@ -24,18 +27,24 @@ export class OptimizationResultView extends Widget {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.disposable(
|
this.disposable(
|
||||||
hunt_optimizer_stores.observe_current(
|
hunt_optimizer_stores.current.observe(
|
||||||
hunt_optimizer_store => {
|
async ({ value }) => {
|
||||||
if (this.results_observer) {
|
try {
|
||||||
this.results_observer.dispose();
|
const hunt_optimizer_store = await value;
|
||||||
}
|
|
||||||
|
|
||||||
this.results_observer = hunt_optimizer_store.result.observe(
|
if (this.results_observer) {
|
||||||
({ value }) => this.update_table(value),
|
this.results_observer.dispose();
|
||||||
{
|
}
|
||||||
call_now: true,
|
|
||||||
},
|
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 },
|
{ call_now: true },
|
||||||
),
|
),
|
||||||
|
@ -11,6 +11,9 @@ import { ItemType } from "../../core/model/items";
|
|||||||
import { Disposable } from "../../core/observable/Disposable";
|
import { Disposable } from "../../core/observable/Disposable";
|
||||||
import { ServerMap } from "../../core/stores/ServerMap";
|
import { ServerMap } from "../../core/stores/ServerMap";
|
||||||
import { HuntOptimizerStore } from "../stores/HuntOptimizerStore";
|
import { HuntOptimizerStore } from "../stores/HuntOptimizerStore";
|
||||||
|
import Logger from "js-logger";
|
||||||
|
|
||||||
|
const logger = Logger.get("hunt_optimizer/gui/WantedItemsView");
|
||||||
|
|
||||||
export class WantedItemsView extends Widget {
|
export class WantedItemsView extends Widget {
|
||||||
readonly element = el.div({ class: "hunt_optimizer_WantedItemsView" });
|
readonly element = el.div({ class: "hunt_optimizer_WantedItemsView" });
|
||||||
@ -48,29 +51,34 @@ export class WantedItemsView extends Widget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.disposables(
|
this.disposables(
|
||||||
hunt_optimizer_stores.observe_current(
|
hunt_optimizer_stores.current.observe(
|
||||||
hunt_optimizer_store => {
|
async ({ value }) => {
|
||||||
this.store_disposer.dispose_all();
|
try {
|
||||||
|
const hunt_optimizer_store = await value;
|
||||||
|
this.store_disposer.dispose_all();
|
||||||
|
|
||||||
this.store_disposer.add_all(
|
this.store_disposer.add_all(
|
||||||
bind_children_to(
|
bind_children_to(
|
||||||
this.tbody_element,
|
this.tbody_element,
|
||||||
hunt_optimizer_store.wanted_items,
|
hunt_optimizer_store.wanted_items,
|
||||||
this.create_row,
|
this.create_row,
|
||||||
),
|
),
|
||||||
|
|
||||||
combo_box.selected.observe(({ value: item_type }) => {
|
combo_box.selected.observe(({ value: item_type }) => {
|
||||||
if (item_type) {
|
if (item_type) {
|
||||||
hunt_optimizer_store.add_wanted_item(item_type);
|
hunt_optimizer_store.add_wanted_item(item_type);
|
||||||
combo_box.selected.val = undefined;
|
combo_box.selected.val = undefined;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
huntable_items.val = hunt_optimizer_store.huntable_item_types
|
huntable_items.val = hunt_optimizer_store.huntable_item_types
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
filtered_huntable_items.val = huntable_items.val;
|
filtered_huntable_items.val = huntable_items.val;
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Couldn't load hunt optimizer store.", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ call_now: true },
|
{ call_now: true },
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { HuntOptimizerView } from "./gui/HuntOptimizerView";
|
import { HuntOptimizerView } from "./gui/HuntOptimizerView";
|
||||||
import { ServerMap } from "../core/stores/ServerMap";
|
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 { 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 { ItemTypeStore } from "../core/stores/ItemTypeStore";
|
||||||
import { HuntMethodPersister } from "./persistence/HuntMethodPersister";
|
import { HuntMethodPersister } from "./persistence/HuntMethodPersister";
|
||||||
import { HuntOptimizerPersister } from "./persistence/HuntOptimizerPersister";
|
import { HuntOptimizerPersister } from "./persistence/HuntOptimizerPersister";
|
||||||
@ -15,12 +15,12 @@ export function initialize_hunt_optimizer(
|
|||||||
item_type_stores: ServerMap<ItemTypeStore>,
|
item_type_stores: ServerMap<ItemTypeStore>,
|
||||||
item_drop_stores: ServerMap<ItemDropStore>,
|
item_drop_stores: ServerMap<ItemDropStore>,
|
||||||
): HuntOptimizerView {
|
): HuntOptimizerView {
|
||||||
const hunt_method_stores: ServerMap<HuntMethodStore> = load_hunt_method_stores(
|
const hunt_method_stores: ServerMap<HuntMethodStore> = create_hunt_method_stores(
|
||||||
http_client,
|
http_client,
|
||||||
gui_store,
|
gui_store,
|
||||||
new HuntMethodPersister(),
|
new HuntMethodPersister(),
|
||||||
);
|
);
|
||||||
const hunt_optimizer_stores: ServerMap<HuntOptimizerStore> = load_hunt_optimizer_stores(
|
const hunt_optimizer_stores: ServerMap<HuntOptimizerStore> = create_hunt_optimizer_stores(
|
||||||
gui_store,
|
gui_store,
|
||||||
new HuntOptimizerPersister(item_type_stores),
|
new HuntOptimizerPersister(item_type_stores),
|
||||||
item_type_stores,
|
item_type_stores,
|
||||||
|
@ -20,7 +20,7 @@ const DEFAULT_DURATION = Duration.fromObject({ minutes: 30 });
|
|||||||
const DEFAULT_GOVERNMENT_TEST_DURATION = Duration.fromObject({ minutes: 45 });
|
const DEFAULT_GOVERNMENT_TEST_DURATION = Duration.fromObject({ minutes: 45 });
|
||||||
const DEFAULT_LARGE_ENEMY_COUNT_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,
|
http_client: HttpClient,
|
||||||
gui_store: GuiStore,
|
gui_store: GuiStore,
|
||||||
hunt_method_persister: HuntMethodPersister,
|
hunt_method_persister: HuntMethodPersister,
|
||||||
|
@ -25,7 +25,7 @@ import { ServerMap } from "../../core/stores/ServerMap";
|
|||||||
import { GuiStore } from "../../core/stores/GuiStore";
|
import { GuiStore } from "../../core/stores/GuiStore";
|
||||||
import { HuntOptimizerPersister } from "../persistence/HuntOptimizerPersister";
|
import { HuntOptimizerPersister } from "../persistence/HuntOptimizerPersister";
|
||||||
|
|
||||||
export function load_hunt_optimizer_stores(
|
export function create_hunt_optimizer_stores(
|
||||||
gui_store: GuiStore,
|
gui_store: GuiStore,
|
||||||
hunt_optimizer_persister: HuntOptimizerPersister,
|
hunt_optimizer_persister: HuntOptimizerPersister,
|
||||||
item_type_stores: ServerMap<ItemTypeStore>,
|
item_type_stores: ServerMap<ItemTypeStore>,
|
||||||
|
@ -10,7 +10,7 @@ import { HttpClient } from "../../core/HttpClient";
|
|||||||
|
|
||||||
const logger = Logger.get("stores/ItemDropStore");
|
const logger = Logger.get("stores/ItemDropStore");
|
||||||
|
|
||||||
export function load_item_drop_stores(
|
export function create_item_drop_stores(
|
||||||
http_client: HttpClient,
|
http_client: HttpClient,
|
||||||
gui_store: GuiStore,
|
gui_store: GuiStore,
|
||||||
item_type_stores: ServerMap<ItemTypeStore>,
|
item_type_stores: ServerMap<ItemTypeStore>,
|
||||||
|
@ -8,24 +8,32 @@ import { initialize } from "./initialize";
|
|||||||
import { StubHttpClient } from "./core/HttpClient";
|
import { StubHttpClient } from "./core/HttpClient";
|
||||||
import { DisposableThreeRenderer } from "./core/rendering/Renderer";
|
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 logged_errors: string[] = [];
|
const with_path = path == undefined ? "without specific path" : `with path ${path}`;
|
||||||
|
|
||||||
Logger.setHandler((messages: any[], context: IContext) => {
|
test(`Initialization and shutdown ${with_path} should succeed without throwing errors or logging with level WARN or above.`, () => {
|
||||||
if (context.level.value >= Logger.ERROR.value) {
|
const logged_errors: string[] = [];
|
||||||
logged_errors.push(Array.prototype.join.call(messages, " "));
|
|
||||||
|
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 {
|
class StubRenderer implements DisposableThreeRenderer {
|
||||||
domElement: HTMLCanvasElement = document.createElement("canvas");
|
domElement: HTMLCanvasElement = document.createElement("canvas");
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { HttpClient } from "./core/HttpClient";
|
import { HttpClient } from "./core/HttpClient";
|
||||||
import { Disposable } from "./core/observable/Disposable";
|
import { Disposable } from "./core/observable/Disposable";
|
||||||
import { GuiStore, GuiTool } from "./core/stores/GuiStore";
|
import { GuiStore, GuiTool } from "./core/stores/GuiStore";
|
||||||
import { load_item_type_stores } from "./core/stores/ItemTypeStore";
|
import { create_item_type_stores } from "./core/stores/ItemTypeStore";
|
||||||
import { load_item_drop_stores } from "./hunt_optimizer/stores/ItemDropStore";
|
import { create_item_drop_stores } from "./hunt_optimizer/stores/ItemDropStore";
|
||||||
import { ApplicationView } from "./application/gui/ApplicationView";
|
import { ApplicationView } from "./application/gui/ApplicationView";
|
||||||
import { throttle } from "lodash";
|
import { throttle } from "lodash";
|
||||||
import { DisposableThreeRenderer } from "./core/rendering/Renderer";
|
import { DisposableThreeRenderer } from "./core/rendering/Renderer";
|
||||||
@ -23,8 +23,8 @@ export function initialize(
|
|||||||
|
|
||||||
// Initialize core stores shared by several submodules.
|
// Initialize core stores shared by several submodules.
|
||||||
const gui_store = new GuiStore();
|
const gui_store = new GuiStore();
|
||||||
const item_type_stores = load_item_type_stores(http_client, gui_store);
|
const item_type_stores = create_item_type_stores(http_client, gui_store);
|
||||||
const item_drop_stores = load_item_drop_stores(http_client, gui_store, item_type_stores);
|
const item_drop_stores = create_item_drop_stores(http_client, gui_store, item_type_stores);
|
||||||
|
|
||||||
// Initialize application view.
|
// Initialize application view.
|
||||||
const application_view = new ApplicationView(gui_store, [
|
const application_view = new ApplicationView(gui_store, [
|
||||||
|
Loading…
Reference in New Issue
Block a user