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"],
|
||||
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",
|
||||
},
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 },
|
||||
),
|
||||
|
@ -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 },
|
||||
),
|
||||
|
@ -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 },
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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>,
|
||||
|
@ -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>,
|
||||
|
@ -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");
|
||||
|
@ -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, [
|
||||
|
Loading…
Reference in New Issue
Block a user