From 2a2b5523e5313c870209424589d6b8c8dcd5a591 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Wed, 23 Dec 2020 15:53:17 +0100 Subject: [PATCH 1/7] Created RuntimeJob --- packages/core/src/collection/selector.ts | 14 +---- packages/core/src/event/event.observer.ts | 31 ++++++++-- packages/core/src/internal.ts | 3 +- packages/core/src/runtime/index.ts | 32 ++++------ packages/core/src/runtime/observer.ts | 4 +- .../src/runtime/{job.ts => runtime.job.ts} | 34 +++++------ packages/core/src/state/index.ts | 17 +----- packages/core/src/state/state.observer.ts | 58 ++++++++++++------- packages/core/src/state/state.runtime.job.ts | 51 ++++++++++++++++ .../tests/new/collection/collection.test.ts | 22 +++++++ packages/core/tests/new/runtime/job.test.ts | 14 ++--- .../core/tests/new/runtime/observer.test.ts | 9 ++- .../core/tests/new/runtime/runtime.test.ts | 50 ++++++++-------- .../tests/new/state/state.observer.test.ts | 14 +++-- packages/core/tests/new/state/state.test.ts | 1 - packages/multieditor/src/status/index.ts | 4 +- .../multieditor/src/status/status.observer.ts | 26 +++++++-- 17 files changed, 246 insertions(+), 138 deletions(-) rename packages/core/src/runtime/{job.ts => runtime.job.ts} (65%) create mode 100644 packages/core/src/state/state.runtime.job.ts diff --git a/packages/core/src/collection/selector.ts b/packages/core/src/collection/selector.ts index dd8e17c2..520caf88 100644 --- a/packages/core/src/collection/selector.ts +++ b/packages/core/src/collection/selector.ts @@ -82,18 +82,6 @@ export class Selector extends State< return this; } - // Overwrite old Item Values with new Item Value - if (config.overwrite) { - this._value = copy(newItem._value); - this.nextStateValue = copy(newItem._value); - this.previousStateValue = copy(newItem._value); - this.initialStateValue = copy(newItem._value); - this.isSet = false; - this.isPlaceholder = false; - - config.force = true; - } - // Remove old Item from Collection if it is an Placeholder if (oldItem?.isPlaceholder) delete this.collection().data[this._itemKey]; @@ -113,6 +101,7 @@ export class Selector extends State< background: config.background, sideEffects: config.sideEffects, force: config.force, + overwrite: config.overwrite, }); return this; @@ -132,6 +121,7 @@ export class Selector extends State< background: false, force: false, storage: true, + overwrite: false, }); // Set Selector Value to undefined if Item doesn't exist diff --git a/packages/core/src/event/event.observer.ts b/packages/core/src/event/event.observer.ts index 4592b38e..aa750fcc 100644 --- a/packages/core/src/event/event.observer.ts +++ b/packages/core/src/event/event.observer.ts @@ -1,10 +1,12 @@ import { Observer, - Job, + RuntimeJob, ObserverKey, Event, SubscriptionContainer, IngestConfigInterface, + RuntimeJobConfigInterface, + defineConfig, } from "../internal"; export class EventObserver extends Observer { @@ -36,8 +38,25 @@ export class EventObserver extends Observer { * Ingests Event into Runtime and causes Rerender on Components that got subscribed by the Event (Observer) * @param config - Config */ - public trigger(config: IngestConfigInterface = {}): void { - this.agileInstance().runtime.ingest(this, config); + public trigger(config: EventIngestConfigInterface = {}): void { + config = defineConfig(config, { + perform: true, + background: false, + sideEffects: true, + force: false, + }); + + // Create Job + const job = new RuntimeJob(this, { + force: config.force, + sideEffects: config.sideEffects, + background: config.background, + key: this._key, + }); + + this.agileInstance().runtime.ingest(job, { + perform: config.perform, + }); } //========================================================================================================= @@ -48,7 +67,7 @@ export class EventObserver extends Observer { * Performs Job from Runtime * @param job - Job that gets performed */ - public perform(job: Job) { + public perform(job: RuntimeJob) { // Noting to perform } } @@ -63,3 +82,7 @@ export interface CreateEventObserverConfigInterface { subs?: Array; key?: ObserverKey; } + +export interface EventIngestConfigInterface + extends RuntimeJobConfigInterface, + IngestConfigInterface {} diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index 564a1655..c427f8a3 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -16,7 +16,7 @@ export * from "./agile"; // Runtime export * from "./runtime"; export * from "./runtime/observer"; -export * from "./runtime/job"; +export * from "./runtime/runtime.job"; export * from "./runtime/subscription/container/SubscriptionContainer"; export * from "./runtime/subscription/container/CallbackSubscriptionContainer"; export * from "./runtime/subscription/container/ComponentSubscriptionContainer"; @@ -31,6 +31,7 @@ export * from "./storages/persistent"; export * from "./state"; export * from "./state/state.observer"; export * from "./state/state.persistent"; +export * from "./state/state.runtime.job"; // Computed export * from "./computed"; diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 316ee9d7..d26ca8d5 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -1,11 +1,9 @@ import { Agile, SubscriptionContainer, - Observer, - Job, + RuntimeJob, CallbackSubscriptionContainer, ComponentSubscriptionContainer, - CreateJobConfigInterface, defineConfig, } from "../internal"; @@ -13,10 +11,10 @@ export class Runtime { public agileInstance: () => Agile; // Queue system - public currentJob: Job | null = null; - public jobQueue: Array = []; - public notReadyJobsToRerender: Set = new Set(); // Jobs that got performed but aren't ready to get rerendered (wait for mount) - public jobsToRerender: Array = []; // Jobs that are performed and will be rendered + public currentJob: RuntimeJob | null = null; + public jobQueue: Array = []; + public notReadyJobsToRerender: Set = new Set(); // Jobs that got performed but aren't ready to get rerendered (wait for mount) + public jobsToRerender: Array = []; // Jobs that are performed and will be rendered /** * @internal @@ -32,23 +30,15 @@ export class Runtime { //========================================================================================================= /** * @internal - * Ingests Observer into Runtime - * -> Creates Job which will be performed by the Runtime - * @param observer - Observer that gets performed by the Runtime + * Ingests Job into Runtime that gets performed + * @param job - Job * @param config - Config */ - public ingest(observer: Observer, config: IngestConfigInterface = {}): void { + public ingest(job: RuntimeJob, config: IngestConfigInterface = {}): void { config = defineConfig(config, { perform: true, }); - const job = new Job(observer, { - storage: config.storage, - sideEffects: config.sideEffects, - force: config.force, - background: config.background, - key: config.key || observer._key, - }); this.jobQueue.push(job); // Logging @@ -69,7 +59,7 @@ export class Runtime { * Performs Job and adds it to the rerender queue if necessary * @param job - Job that gets performed */ - public perform(job: Job): void { + public perform(job: RuntimeJob): void { this.currentJob = job; // Perform Job @@ -180,7 +170,7 @@ export class Runtime { */ public handleObjectBasedSubscription( subscriptionContainer: SubscriptionContainer, - job: Job + job: RuntimeJob ): void { let foundKey: string | null = null; @@ -226,6 +216,6 @@ export class Runtime { /** * @param perform - If Job gets performed immediately */ -export interface IngestConfigInterface extends CreateJobConfigInterface { +export interface IngestConfigInterface { perform?: boolean; } diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts index 7c5a3d33..fd60f06a 100644 --- a/packages/core/src/runtime/observer.ts +++ b/packages/core/src/runtime/observer.ts @@ -1,7 +1,7 @@ import { Agile, StateKey, - Job, + RuntimeJob, SubscriptionContainer, defineConfig, } from "../internal"; @@ -64,7 +64,7 @@ export class Observer { * Performs Job of Runtime * @param job - Job that gets performed */ - public perform(job: Job) { + public perform(job: RuntimeJob) { Agile.logger.warn( "Perform function isn't Set in Observer! Be aware that Observer is no stand alone class!" ); diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/runtime.job.ts similarity index 65% rename from packages/core/src/runtime/job.ts rename to packages/core/src/runtime/runtime.job.ts index 4e4228c0..abad450b 100644 --- a/packages/core/src/runtime/job.ts +++ b/packages/core/src/runtime/runtime.job.ts @@ -1,9 +1,9 @@ import { Observer, defineConfig, SubscriptionContainer } from "../internal"; -export class Job { - public _key?: JobKey; +export class RuntimeJob { + public _key?: RuntimeJobKey; public observer: ObserverType; - public config: JobConfigInterface; + public config: RuntimeJobConfigInterface; public rerender: boolean; // If Job will cause rerender on subscriptionContainer in Observer public performed = false; // If Job has been performed by Runtime public subscriptionContainersToUpdate = new Set(); // SubscriptionContainer that have to be updated/rerendered @@ -14,18 +14,19 @@ export class Job { * @param observer - Observer that is represented by this Job and gets performed * @param config - Config */ - constructor(observer: ObserverType, config: CreateJobConfigInterface = {}) { - config = defineConfig(config, { + constructor( + observer: ObserverType, + config: CreateRuntimeJobConfigInterface = {} + ) { + config = defineConfig(config, { background: false, sideEffects: true, force: false, - storage: true, }); this.config = { background: config.background, force: config.force, sideEffects: config.sideEffects, - storage: config.storage, }; this.observer = observer; @@ -36,33 +37,32 @@ export class Job { this.subscriptionContainersToUpdate = new Set(observer.subs); } - public get key(): JobKey | undefined { + public get key(): RuntimeJobKey | undefined { return this._key; } - public set key(value: JobKey | undefined) { + public set key(value: RuntimeJobKey | undefined) { this._key = value; } } -export type JobKey = string | number; +export type RuntimeJobKey = string | number; /** - * @param key - Key/Name of Job + * @param key - Key/Name of RuntimeJob */ -export interface CreateJobConfigInterface extends JobConfigInterface { - key?: JobKey; +export interface CreateRuntimeJobConfigInterface + extends RuntimeJobConfigInterface { + key?: RuntimeJobKey; } /** * @param background - If Job gets executed in the background -> not causing any rerender - * @param sideEffects - If SideEffects gets executed - * @param storage - If Job value gets saved in Storage + * @param sideEffects - If SideEffects get executed * @param force - Force performing Job */ -export interface JobConfigInterface { +export interface RuntimeJobConfigInterface { background?: boolean; sideEffects?: boolean; - storage?: boolean; force?: boolean; } diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 263536ad..cbf467b7 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -12,9 +12,9 @@ import { isFunction, notEqual, generateId, - JobConfigInterface, PersistentKey, ComputedTracker, + StateIngestConfigInterface, } from "../internal"; export class State { @@ -157,24 +157,13 @@ export class State { Agile.logger.warn(message); } - // Overwrite old Item Values with new Item Value - if (config.overwrite) { - this._value = copy(value); - this.nextStateValue = copy(value); - this.previousStateValue = copy(value); - this.initialStateValue = copy(value); - this.isSet = false; - this.isPlaceholder = false; - - config.force = true; - } - // Ingest new value into Runtime this.observer.ingestValue(value, { storage: config.storage, background: config.background, force: config.force, sideEffects: config.sideEffects, + overwrite: config.overwrite, }); return this; @@ -188,7 +177,7 @@ export class State { * Ingests nextStateValue, computedValue into Runtime * @param config - Config */ - public ingest(config: JobConfigInterface = {}): this { + public ingest(config: StateIngestConfigInterface = {}): this { config = defineConfig(config, { sideEffects: true, background: false, diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 32505bb7..c137b5cf 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -2,7 +2,6 @@ import { Observer, State, Computed, - Job, copy, defineConfig, ObserverKey, @@ -11,6 +10,8 @@ import { isFunction, SubscriptionContainer, IngestConfigInterface, + StateRuntimeJob, + StateRuntimeJobConfigInterface, } from "../internal"; export class StateObserver extends Observer { @@ -45,7 +46,7 @@ export class StateObserver extends Observer { * Ingests nextStateValue or computedValue into Runtime and applies it to the State * @param config - Config */ - public ingest(config: IngestConfigInterface = {}): void { + public ingest(config: StateIngestConfigInterface = {}): void { const state = this.state(); let newStateValue: ValueType; @@ -66,7 +67,7 @@ export class StateObserver extends Observer { */ public ingestValue( newStateValue: ValueType, - config: IngestConfigInterface = {} + config: StateIngestConfigInterface = {} ): void { const state = this.state(); config = defineConfig(config, { @@ -75,6 +76,7 @@ export class StateObserver extends Observer { sideEffects: true, force: false, storage: true, + overwrite: state.isPlaceholder, }); // Assign next State Value and compute it if necessary @@ -85,7 +87,19 @@ export class StateObserver extends Observer { // Check if State Value and new/next Value are equals if (equal(state._value, this.nextStateValue) && !config.force) return; - this.agileInstance().runtime.ingest(this, config); + // Create Job + const job = new StateRuntimeJob(this, { + storage: config.storage, + sideEffects: config.sideEffects, + force: config.force, + background: config.background, + overwrite: config.overwrite, + key: this._key, + }); + + this.agileInstance().runtime.ingest(job, { + perform: config.perform, + }); } //========================================================================================================= @@ -93,32 +107,30 @@ export class StateObserver extends Observer { //========================================================================================================= /** * @internal - * Performs Job from Runtime that holds this Observer - * @param job - Job that gets performed + * Performs Job that holds this Observer + * @param job - Job */ - public perform(job: Job) { + public perform(job: StateRuntimeJob) { const state = job.observer.state(); - // Set Previous State + // Assign new State Values state.previousStateValue = copy(state._value); - - // Set new State Value state._value = copy(job.observer.nextStateValue); state.nextStateValue = copy(job.observer.nextStateValue); + job.observer.value = copy(job.observer.nextStateValue); - state.isSet = notEqual( - job.observer.nextStateValue, - state.initialStateValue - ); - - // Reset isPlaceholder and set initial/previous Value to nextValue because the placeholder State had no proper value before - if (state.isPlaceholder) { + // Overwrite old State Values + if (job.config.overwrite) { state.initialStateValue = copy(state._value); state.previousStateValue = copy(state._value); state.isPlaceholder = false; } - job.observer.value = copy(job.observer.nextStateValue); + state.isSet = notEqual( + state._value, + state.initialStateValue + ); + this.sideEffects(job); } @@ -127,10 +139,10 @@ export class StateObserver extends Observer { //========================================================================================================= /** * @internal - * SideEffects of Perform Function - * @param job - Job whose SideEffects gets executed + * SideEffects of Job + * @param job - Job */ - public sideEffects(job: Job) { + public sideEffects(job: StateRuntimeJob) { const state = job.observer.state(); // Call Watchers Functions @@ -162,3 +174,7 @@ export interface CreateStateObserverConfigInterface { subs?: Array; key?: ObserverKey; } + +export interface StateIngestConfigInterface + extends StateRuntimeJobConfigInterface, + IngestConfigInterface {} diff --git a/packages/core/src/state/state.runtime.job.ts b/packages/core/src/state/state.runtime.job.ts new file mode 100644 index 00000000..1ec13473 --- /dev/null +++ b/packages/core/src/state/state.runtime.job.ts @@ -0,0 +1,51 @@ +import { + defineConfig, + RuntimeJob, + RuntimeJobConfigInterface, + RuntimeJobKey, + StateObserver, +} from "../internal"; + +export class StateRuntimeJob extends RuntimeJob { + public config: StateRuntimeJobConfigInterface; + + constructor( + observer: StateObserver, + config: CreateStateRuntimeJobConfigInterface = {} + ) { + super(observer, config); + config = defineConfig(config, { + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + + this.config = { + background: config.background, + force: config.force, + sideEffects: config.sideEffects, + storage: config.storage, + overwrite: config.overwrite, + }; + } +} + +/** + * @param key - Key/Name of Job + */ +export interface CreateStateRuntimeJobConfigInterface + extends StateRuntimeJobConfigInterface { + key?: RuntimeJobKey; +} + +/** + * @param overwrite - If State gets overwritten with value + * @param storage - If Job value gets saved in Storage + */ +export interface StateRuntimeJobConfigInterface + extends RuntimeJobConfigInterface { + overwrite?: boolean; + storage?: boolean; +} diff --git a/packages/core/tests/new/collection/collection.test.ts b/packages/core/tests/new/collection/collection.test.ts index 2d5eff98..0c97f8f6 100644 --- a/packages/core/tests/new/collection/collection.test.ts +++ b/packages/core/tests/new/collection/collection.test.ts @@ -713,5 +713,27 @@ describe("Collection Tests", () => { ); }); }); + + describe("createGroup function tests", () => { + let dummyGroup: Group; + + beforeEach(() => { + dummyGroup = new Group(collection, [], { key: "dummyGroup" }); + + collection.groups = { + dummyGroup: dummyGroup, + }; + + dummyGroup.set = jest.fn(); + }); + + it("should create Group if it doesn't exist", () => { + const response = collection.createGroup("newGroup", ["test1"]); + + expect(response._key).toBe("newGroup"); + expect(response.isPlaceholder).toBeFalsy(); + expect(response._value).toStrictEqual(["test1"]); + }); + }); }); }); diff --git a/packages/core/tests/new/runtime/job.test.ts b/packages/core/tests/new/runtime/job.test.ts index 46964f7c..124dcda8 100644 --- a/packages/core/tests/new/runtime/job.test.ts +++ b/packages/core/tests/new/runtime/job.test.ts @@ -1,4 +1,4 @@ -import { Agile, Integration, Job, Observer } from "../../../src"; +import { Agile, Integration, RuntimeJob, Observer } from "../../../src"; describe("Job Tests", () => { let dummyAgile: Agile; @@ -16,7 +16,7 @@ describe("Job Tests", () => { it("should create Job with Agile that has integrations (default config)", () => { dummyAgile.integrate(dummyIntegration); - const job = new Job(dummyObserver); + const job = new RuntimeJob(dummyObserver); expect(job._key).toBeUndefined(); expect(job.observer).toBe(dummyObserver); @@ -34,7 +34,7 @@ describe("Job Tests", () => { it("should create Job with Agile that has integrations (specific config)", () => { dummyAgile.integrate(dummyIntegration); - const job = new Job(dummyObserver, { + const job = new RuntimeJob(dummyObserver, { key: "dummyJob", sideEffects: false, force: true, @@ -55,7 +55,7 @@ describe("Job Tests", () => { }); it("should create Job with Agile that has no integrations (default config)", () => { - const job = new Job(dummyObserver); + const job = new RuntimeJob(dummyObserver); expect(job._key).toBeUndefined(); expect(job.observer).toBe(dummyObserver); @@ -73,7 +73,7 @@ describe("Job Tests", () => { it("should create Job and Agile that has integrations (config.background = true)", () => { dummyAgile.integrate(dummyIntegration); - const job = new Job(dummyObserver, { background: true }); + const job = new RuntimeJob(dummyObserver, { background: true }); expect(job._key).toBeUndefined(); expect(job.observer).toBe(dummyObserver); @@ -89,10 +89,10 @@ describe("Job Tests", () => { }); describe("Job Function Tests", () => { - let job: Job; + let job: RuntimeJob; beforeEach(() => { - job = new Job(dummyObserver); + job = new RuntimeJob(dummyObserver); }); describe("key get function tests", () => { diff --git a/packages/core/tests/new/runtime/observer.test.ts b/packages/core/tests/new/runtime/observer.test.ts index ffdb386d..5b370025 100644 --- a/packages/core/tests/new/runtime/observer.test.ts +++ b/packages/core/tests/new/runtime/observer.test.ts @@ -1,4 +1,9 @@ -import { Observer, Agile, SubscriptionContainer, Job } from "../../../src"; +import { + Observer, + Agile, + SubscriptionContainer, + RuntimeJob, +} from "../../../src"; describe("Observer Tests", () => { let dummyAgile: Agile; @@ -73,7 +78,7 @@ describe("Observer Tests", () => { describe("perform function tests", () => { it("should print warning", () => { - const dummyJob = new Job(observer); + const dummyJob = new RuntimeJob(observer); observer.perform(dummyJob); diff --git a/packages/core/tests/new/runtime/runtime.test.ts b/packages/core/tests/new/runtime/runtime.test.ts index b3674b30..f50b04da 100644 --- a/packages/core/tests/new/runtime/runtime.test.ts +++ b/packages/core/tests/new/runtime/runtime.test.ts @@ -2,7 +2,7 @@ import { Agile, CallbackSubscriptionContainer, ComponentSubscriptionContainer, - Job, + RuntimeJob, Observer, Runtime, SubscriptionContainer, @@ -41,10 +41,10 @@ describe("Runtime Tests", () => { }); describe("ingest function tests", () => { - let dummyJob: Job; + let dummyJob: RuntimeJob; beforeEach(() => { - dummyJob = new Job(dummyObserver1); + dummyJob = new RuntimeJob(dummyObserver1); runtime.perform = jest.fn(); runtime.jobQueue.shift = jest.fn(() => dummyJob); @@ -70,14 +70,14 @@ describe("Runtime Tests", () => { }); describe("perform function tests", () => { - let dummyJob1: Job; - let dummyJob2: Job; - let dummyJob3: Job; + let dummyJob1: RuntimeJob; + let dummyJob2: RuntimeJob; + let dummyJob3: RuntimeJob; beforeEach(() => { - dummyJob1 = new Job(dummyObserver1, { key: "dummyJob1" }); - dummyJob2 = new Job(dummyObserver2, { key: "dummyJob2" }); - dummyJob3 = new Job(dummyObserver1, { key: "dummyJob3" }); + dummyJob1 = new RuntimeJob(dummyObserver1, { key: "dummyJob1" }); + dummyJob2 = new RuntimeJob(dummyObserver2, { key: "dummyJob2" }); + dummyJob3 = new RuntimeJob(dummyObserver1, { key: "dummyJob3" }); dummyJob1.rerender = true; dummyJob2.rerender = true; dummyJob3.rerender = false; @@ -135,10 +135,10 @@ describe("Runtime Tests", () => { describe("updateSubscribers function tests", () => { let dummyObserver4: Observer; - let rCallbackSubJob: Job; - let nrArCallbackSubJob: Job; - let rComponentSubJob: Job; - let nrArComponentSubJob: Job; + let rCallbackSubJob: RuntimeJob; + let nrArCallbackSubJob: RuntimeJob; + let rComponentSubJob: RuntimeJob; + let nrArComponentSubJob: RuntimeJob; let rCallbackSubContainer: CallbackSubscriptionContainer; let rCallbackSubContainerCallbackFunction = () => {}; let nrCallbackSubContainer: CallbackSubscriptionContainer; @@ -194,10 +194,14 @@ describe("Runtime Tests", () => { ).subscriptionContainer as ComponentSubscriptionContainer; nrComponentSubContainer.ready = false; - rComponentSubJob = new Job(dummyObserver3, { key: "dummyJob3" }); // Job with ready Component Subscription - rCallbackSubJob = new Job(dummyObserver1, { key: "dummyJob1" }); // Job with ready CallbackSubscription - nrArComponentSubJob = new Job(dummyObserver4, { key: "dummyJob4" }); // Job with not ready and ready Component Subscription - nrArCallbackSubJob = new Job(dummyObserver2, { key: "dummyJob2" }); // Job with not ready and ready Callback Subscription + rComponentSubJob = new RuntimeJob(dummyObserver3, { key: "dummyJob3" }); // Job with ready Component Subscription + rCallbackSubJob = new RuntimeJob(dummyObserver1, { key: "dummyJob1" }); // Job with ready CallbackSubscription + nrArComponentSubJob = new RuntimeJob(dummyObserver4, { + key: "dummyJob4", + }); // Job with not ready and ready Component Subscription + nrArCallbackSubJob = new RuntimeJob(dummyObserver2, { + key: "dummyJob2", + }); // Job with not ready and ready Callback Subscription jest.spyOn(dummyAgile.integrations, "update"); jest.spyOn(runtime, "handleObjectBasedSubscription"); @@ -358,16 +362,16 @@ describe("Runtime Tests", () => { let dummyComponent2 = { my: "second cool component", }; - let arrayJob: Job; - let objectJob1: Job; - let objectJob2: Job; + let arrayJob: RuntimeJob; + let objectJob1: RuntimeJob; + let objectJob2: RuntimeJob; beforeEach(() => { arraySubscriptionContainer = dummyAgile.subController.subscribeWithSubsArray( dummyComponent, [dummyObserver1, dummyObserver2, dummyObserver3] ); - arrayJob = new Job(dummyObserver1, { key: "dummyArrayJob" }); + arrayJob = new RuntimeJob(dummyObserver1, { key: "dummyArrayJob" }); objectSubscriptionContainer = dummyAgile.subController.subscribeWithSubsObject( dummyComponent2, @@ -377,8 +381,8 @@ describe("Runtime Tests", () => { observer3: dummyObserver3, } ).subscriptionContainer; - objectJob1 = new Job(dummyObserver1, { key: "dummyObjectJob1" }); - objectJob2 = new Job(dummyObserver3, { key: "dummyObjectJob2" }); + objectJob1 = new RuntimeJob(dummyObserver1, { key: "dummyObjectJob1" }); + objectJob2 = new RuntimeJob(dummyObserver3, { key: "dummyObjectJob2" }); }); it("should ignore not object based SubscriptionContainer", () => { diff --git a/packages/core/tests/new/state/state.observer.test.ts b/packages/core/tests/new/state/state.observer.test.ts index 5c84a7a8..7f3ab2ed 100644 --- a/packages/core/tests/new/state/state.observer.test.ts +++ b/packages/core/tests/new/state/state.observer.test.ts @@ -1,7 +1,7 @@ import { Agile, Computed, - Job, + RuntimeJob, Observer, State, StateObserver, @@ -182,10 +182,12 @@ describe("StateObserver Tests", () => { }); describe("perform function tests", () => { - let dummyJob: Job; + let dummyJob: RuntimeJob; beforeEach(() => { - dummyJob = new Job(stateObserver, { key: "dummyJob" }); + dummyJob = new RuntimeJob(stateObserver, { + key: "dummyJob", + }); dummyState.persistent = new StatePersistent(dummyState); dummyState.isPersisted = true; @@ -243,12 +245,14 @@ describe("StateObserver Tests", () => { }); describe("sideEffects function tests", () => { - let dummyJob: Job; + let dummyJob: RuntimeJob; let dummyStateObserver: StateObserver; beforeEach(() => { dummyStateObserver = new StateObserver(new State(dummyAgile, "test")); - dummyJob = new Job(stateObserver, { key: "dummyJob" }); + dummyJob = new RuntimeJob(stateObserver, { + key: "dummyJob", + }); dummyState.observer.deps.add(dummyStateObserver); diff --git a/packages/core/tests/new/state/state.test.ts b/packages/core/tests/new/state/state.test.ts index fcbf0719..49c58416 100644 --- a/packages/core/tests/new/state/state.test.ts +++ b/packages/core/tests/new/state/state.test.ts @@ -372,7 +372,6 @@ describe("State Tests", () => { it("should update values of State and shouldn't overwrite it if State is placeholder (config.overwrite = false)", () => { numberState.isPlaceholder = true; - // TODO somehow State got called 5 times before here.. numberState.set(20, { overwrite: false }); expect(console.warn).not.toHaveBeenCalled(); diff --git a/packages/multieditor/src/status/index.ts b/packages/multieditor/src/status/index.ts index 6c4f79d3..a0955390 100644 --- a/packages/multieditor/src/status/index.ts +++ b/packages/multieditor/src/status/index.ts @@ -1,4 +1,4 @@ -import {Agile, copy, JobConfigInterface} from "@agile-ts/core"; +import { Agile, copy, RuntimeJobConfigInterface } from "@agile-ts/core"; import { Item, StatusObserver } from "../internal"; export class Status { @@ -67,7 +67,7 @@ export class Status { * Assign last set Status Value to the current Status Value * @param config - Config */ - public assign(config: JobConfigInterface = {}) { + public assign(config: RuntimeJobConfigInterface = {}) { this.observer.assign(config); } diff --git a/packages/multieditor/src/status/status.observer.ts b/packages/multieditor/src/status/status.observer.ts index 0209910c..cb7aaeb9 100644 --- a/packages/multieditor/src/status/status.observer.ts +++ b/packages/multieditor/src/status/status.observer.ts @@ -4,10 +4,11 @@ import { copy, defineConfig, equal, - Job, - JobConfigInterface, + IngestConfigInterface, Observer, ObserverKey, + RuntimeJob, + RuntimeJobConfigInterface, } from "@agile-ts/core"; export class StatusObserver extends Observer { @@ -43,13 +44,12 @@ export class StatusObserver extends Observer { * Assigns next Status Value to current Status Value * @param config - Config */ - public assign(config: JobConfigInterface = {}): void { + public assign(config: StatusIngestConfigInterface = {}): void { config = defineConfig(config, { perform: true, background: false, sideEffects: true, force: false, - storage: true, }); // Set Next Status Value @@ -58,7 +58,17 @@ export class StatusObserver extends Observer { // Check if Status changed if (equal(this.status()._value, this.nextValue) && !config.force) return; - this.agileInstance().runtime.ingest(this, config); + // Create Job + const job = new RuntimeJob(this, { + sideEffects: config.sideEffects, + force: config.force, + background: config.background, + key: this._key, + }); + + this.agileInstance().runtime.ingest(job, { + perform: config.perform, + }); } //========================================================================================================= @@ -69,7 +79,7 @@ export class StatusObserver extends Observer { * Performs Job from Runtime * @param job - Job that gets performed */ - public perform(job: Job) { + public perform(job: RuntimeJob) { const status = job.observer.status(); // Set new State Value @@ -89,3 +99,7 @@ export interface StatusObserverConfigInterface { deps?: Array; key?: ObserverKey; } + +export interface StatusIngestConfigInterface + extends RuntimeJobConfigInterface, + IngestConfigInterface {} From 9c7e99adbb2caa5fceb4198600e27445fddc202e Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Wed, 23 Dec 2020 20:17:29 +0100 Subject: [PATCH 2/7] Fixed _key State instance issues --- packages/core/src/collection/index.ts | 4 +- packages/core/src/collection/item.ts | 14 ++--- packages/core/src/collection/selector.ts | 32 +++------- packages/core/src/runtime/runtime.job.ts | 5 +- packages/core/src/state/index.ts | 63 +++++++------------- packages/core/src/state/state.observer.ts | 20 +++---- packages/core/src/state/state.runtime.job.ts | 6 +- packages/multieditor/src/item.ts | 8 ++- 8 files changed, 56 insertions(+), 96 deletions(-) diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index 81e84748..18399a6f 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -162,7 +162,7 @@ export class Collection { // Set Key/Name of Group to property Name for (let key in groupsObject) - if (!groupsObject[key]._key) groupsObject[key]._key = key; + if (!groupsObject[key]._key) groupsObject[key].setKey(key); this.groups = groupsObject; } @@ -193,7 +193,7 @@ export class Collection { // Set Key/Name of Selector to property Name for (let key in selectorsObject) - if (!selectorsObject[key]._key) selectorsObject[key]._key = key; + if (!selectorsObject[key]._key) selectorsObject[key].setKey(key); this.selectors = selectorsObject; } diff --git a/packages/core/src/collection/item.ts b/packages/core/src/collection/item.ts index 8dcfce8f..952eb81e 100644 --- a/packages/core/src/collection/item.ts +++ b/packages/core/src/collection/item.ts @@ -1,9 +1,4 @@ -import { - State, - Collection, - DefaultItem, - StateKey, -} from "../internal"; +import { State, Collection, DefaultItem, StateKey } from "../internal"; export class Item extends State { static updateGroupSideEffectKey = "rebuildGroup"; @@ -21,10 +16,13 @@ export class Item extends State { data: DataType, config: ItemConfigInterface = {} ) { - super(collection.agileInstance(), data, config); + super(collection.agileInstance(), data, { + isPlaceholder: config.isPlaceholder, + key: data[collection.config.primaryKey], // Set Key/Name of Item to primaryKey of Data + }); this.collection = () => collection; - // Set Key/Name of Item to primaryKey of Data + // Set Key to assign sideEffects this.setKey(data[collection.config.primaryKey]); } diff --git a/packages/core/src/collection/selector.ts b/packages/core/src/collection/selector.ts index 520caf88..d972a3c8 100644 --- a/packages/core/src/collection/selector.ts +++ b/packages/core/src/collection/selector.ts @@ -1,13 +1,12 @@ import { Agile, Collection, - copy, DefaultItem, defineConfig, Item, ItemKey, - SetConfigInterface, State, + StateRuntimeJobConfigInterface, } from "../internal"; export class Selector extends State< @@ -67,7 +66,10 @@ export class Selector extends State< * @param itemKey - New ItemKey * @param config - Config */ - public select(itemKey: ItemKey, config: SelectConfigInterface = {}): this { + public select( + itemKey: ItemKey, + config: StateRuntimeJobConfigInterface = {} + ): this { const oldItem = this.item; let newItem = this.collection().getItemWithReference(itemKey); config = defineConfig(config, { @@ -75,6 +77,7 @@ export class Selector extends State< sideEffects: true, force: false, overwrite: oldItem?.isPlaceholder || false, + storage: true, }); if (this._itemKey === itemKey && !config.force) { @@ -115,15 +118,7 @@ export class Selector extends State< * Rebuilds Selector * @param config - Config */ - public rebuildSelector(config: SetConfigInterface = {}) { - config = defineConfig(config, { - sideEffects: true, - background: false, - force: false, - storage: true, - overwrite: false, - }); - + public rebuildSelector(config: StateRuntimeJobConfigInterface = {}) { // Set Selector Value to undefined if Item doesn't exist if (!this.item || this.item.isPlaceholder) { this.set(undefined, config); @@ -145,16 +140,3 @@ export interface SelectorConfigInterface { key?: SelectorKey; isPlaceholder?: boolean; } - -/** - * @param background - If selecting a new Item happens in the background (-> not causing any rerender) - * @param sideEffects - If Side Effects of Selector get executed - * @param force - Force to select ItemKey - * @param overwrite - If the Selector gets overwritten with the new selected Item (initialStateValue, ..) - */ -export interface SelectConfigInterface { - background?: boolean; - sideEffects?: boolean; - force?: boolean; - overwrite?: boolean; -} diff --git a/packages/core/src/runtime/runtime.job.ts b/packages/core/src/runtime/runtime.job.ts index abad450b..7e80e2a1 100644 --- a/packages/core/src/runtime/runtime.job.ts +++ b/packages/core/src/runtime/runtime.job.ts @@ -10,8 +10,8 @@ export class RuntimeJob { /** * @internal - * Job - Holds Observer and gets executed/performed by the Runtime - * @param observer - Observer that is represented by this Job and gets performed + * Job - Represents Observer that gets performed by the Runtime + * @param observer - Observer * @param config - Config */ constructor( @@ -28,7 +28,6 @@ export class RuntimeJob { force: config.force, sideEffects: config.sideEffects, }; - this.observer = observer; this.rerender = !config.background && diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index cbf467b7..cc60b1ed 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -15,6 +15,7 @@ import { PersistentKey, ComputedTracker, StateIngestConfigInterface, + StateRuntimeJobConfigInterface, } from "../internal"; export class State { @@ -138,13 +139,16 @@ export class State { * @param value - new State Value * @param config - Config */ - public set(value: ValueType, config: SetConfigInterface = {}): this { + public set( + value: ValueType, + config: StateRuntimeJobConfigInterface = {} + ): this { config = defineConfig(config, { sideEffects: true, background: false, force: false, storage: true, - overwrite: this.isPlaceholder, + overwrite: false, }); // Check value has correct Type (js) @@ -158,13 +162,7 @@ export class State { } // Ingest new value into Runtime - this.observer.ingestValue(value, { - storage: config.storage, - background: config.background, - force: config.force, - sideEffects: config.sideEffects, - overwrite: config.overwrite, - }); + this.observer.ingestValue(value, config); return this; } @@ -178,11 +176,6 @@ export class State { * @param config - Config */ public ingest(config: StateIngestConfigInterface = {}): this { - config = defineConfig(config, { - sideEffects: true, - background: false, - force: false, - }); this.observer.ingest(config); return this; } @@ -219,7 +212,7 @@ export class State { * Undoes latest State Value change * @param config - Config */ - public undo(config: SetConfigInterface = {}): this { + public undo(config: StateRuntimeJobConfigInterface = {}): this { this.set(this.previousStateValue, config); return this; } @@ -232,7 +225,7 @@ export class State { * Resets State to its initial Value * @param config - Config */ - public reset(config: SetConfigInterface = {}): this { + public reset(config: StateRuntimeJobConfigInterface = {}): this { this.set(this.initialStateValue, config); return this; } @@ -253,8 +246,11 @@ export class State { ): this { config = defineConfig(config, { addNewProperties: true, + sideEffects: true, background: false, force: false, + storage: true, + overwrite: false, }); if (!isValidObject(this.nextStateValue)) { @@ -271,23 +267,25 @@ export class State { // Merge targetWithChanges into nextStateValue this.nextStateValue = flatMerge( - this.nextStateValue, + copy(this.nextStateValue), targetWithChanges, { addNewProperties: config.addNewProperties } ); - // Check if value has been changed - if (equal(this.value, this.nextStateValue) && !config.force) return this; - // Ingest updated nextStateValue into Runtime - this.ingest({ background: config.background, force: config.force }); + this.ingest({ + background: config.background, + force: config.force, + overwrite: config.overwrite, + sideEffects: config.sideEffects, + storage: config.storage, + }); return this; } //========================================================================================================= // Watch - // https://stackoverflow.com/questions/12688275/is-there-a-way-to-do-method-overloading-in-typescript/12689054#12689054 //========================================================================================================= /** * @public @@ -635,29 +633,10 @@ export interface StateConfigInterface { } /** - * @param background - If assigning a new value happens in the background (-> not causing any rerender) - * @param sideEffects - If Side Effects of State get executed - * @param storage - If State value gets saved in Agile Storage (only useful if State is persisted) - * @param force - Force creating and performing Job - * @param overwrite - If the State gets overwritten with the new Value (initialStateValue, ..) - */ -export interface SetConfigInterface { - background?: boolean; - sideEffects?: boolean; - storage?: boolean; - force?: boolean; - overwrite?: boolean; -} - -/** - * @param background - If assigning new value happens in the background (-> not causing any rerender) * @param addNewProperties - If new Properties gets added to the State Value - * @param force - Force patching Value into State */ -export interface PatchConfigInterface { +export interface PatchConfigInterface extends StateRuntimeJobConfigInterface { addNewProperties?: boolean; - background?: boolean; - force?: boolean; } /** diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index c137b5cf..12d52154 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -28,12 +28,7 @@ export class StateObserver extends Observer { state: State, config: CreateStateObserverConfigInterface = {} ) { - super(state.agileInstance(), { - deps: config.deps, - value: state._value, - key: config.key, - subs: config.subs, - }); + super(state.agileInstance(), { ...config, ...{ value: state._value } }); this.state = () => state; this.nextStateValue = copy(state._value); } @@ -76,9 +71,15 @@ export class StateObserver extends Observer { sideEffects: true, force: false, storage: true, - overwrite: state.isPlaceholder, + overwrite: false, }); + // Force overwriting State because if setting Value the State shouldn't be a placeholder anymore + if (state.isPlaceholder) { + config.force = true; + config.overwrite = true; + } + // Assign next State Value and compute it if necessary this.nextStateValue = state.computeMethod ? copy(state.computeMethod(newStateValue)) @@ -126,10 +127,7 @@ export class StateObserver extends Observer { state.isPlaceholder = false; } - state.isSet = notEqual( - state._value, - state.initialStateValue - ); + state.isSet = notEqual(state._value, state.initialStateValue); this.sideEffects(job); } diff --git a/packages/core/src/state/state.runtime.job.ts b/packages/core/src/state/state.runtime.job.ts index 1ec13473..0cf1e7bb 100644 --- a/packages/core/src/state/state.runtime.job.ts +++ b/packages/core/src/state/state.runtime.job.ts @@ -14,7 +14,7 @@ export class StateRuntimeJob extends RuntimeJob { config: CreateStateRuntimeJobConfigInterface = {} ) { super(observer, config); - config = defineConfig(config, { + config = defineConfig(config, { background: false, sideEffects: true, force: false, @@ -41,8 +41,8 @@ export interface CreateStateRuntimeJobConfigInterface } /** - * @param overwrite - If State gets overwritten with value - * @param storage - If Job value gets saved in Storage + * @param overwrite - If whole State Value gets overwritten with Job Value + * @param storage - If Job Value can be saved in Storage */ export interface StateRuntimeJobConfigInterface extends RuntimeJobConfigInterface { diff --git a/packages/multieditor/src/item.ts b/packages/multieditor/src/item.ts index 71d5c5db..8ec86a6d 100644 --- a/packages/multieditor/src/item.ts +++ b/packages/multieditor/src/item.ts @@ -1,4 +1,8 @@ -import { defineConfig, SetConfigInterface, State } from "@agile-ts/core"; +import { + defineConfig, + State, + StateRuntimeJobConfigInterface, +} from "@agile-ts/core"; import { MultiEditor, Validator, Status, ItemKey } from "./internal"; export class Item extends State { @@ -72,7 +76,7 @@ export class Item extends State { * Resets State to its initial Value * @param config - Config */ - public reset(config: SetConfigInterface = {}): this { + public reset(config: StateRuntimeJobConfigInterface = {}): this { super.reset(config); this.status.display = false; return this; From 1daa83a2e17522968bac0941b90e568b793bb60a Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Wed, 23 Dec 2020 20:35:47 +0100 Subject: [PATCH 3/7] fixed key of Item --- packages/core/src/collection/index.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index 18399a6f..51243aca 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -582,9 +582,16 @@ export class Collection { // Create Placeholder Item to hold reference if (!item) { - const dummyItem = new Item(this, "unknown" as any, { - isPlaceholder: true, - }); + const dummyItem = new Item( + this, + { + [this.config.primaryKey]: itemKey, // Setting ItemKey to assign key to Item + dummy: "item", + } as any, + { + isPlaceholder: true, + } + ); this.data[itemKey] = dummyItem; return dummyItem; } From 46a4df81a2b8b69499131697067a5d7be9090baf Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Wed, 23 Dec 2020 20:56:58 +0100 Subject: [PATCH 4/7] fixed some bugs with notExisting config of getInstance --- packages/core/src/collection/index.ts | 40 +++++++++++++++------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index 51243aca..b865188d 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -270,7 +270,7 @@ export class Collection { changes: DefaultItem | DataType, config: UpdateConfigInterface = {} ): Item | undefined { - const item = this.getItem(itemKey); + const item = this.getItem(itemKey, { notExisting: true }); const primaryKey = this.config.primaryKey; config = defineConfig(config, { addNewProperties: false, @@ -327,7 +327,7 @@ export class Collection { groupKey: GroupKey, initialItems?: Array ): Group { - let group = this.getGroup(groupKey); + let group = this.getGroup(groupKey, { notExisting: true }); // Check if Group already exists if (group) { @@ -359,7 +359,7 @@ export class Collection { selectorKey: SelectorKey, itemKey: ItemKey ): Selector { - let selector = this.getSelector(selectorKey); + let selector = this.getSelector(selectorKey, { notExisting: true }); // Check if Selector already exists if (selector) { @@ -418,7 +418,7 @@ export class Collection { * @param groupKey - Name/Key of Group */ public getGroupWithReference(groupKey: GroupKey): Group { - let group = this.getGroup(groupKey); + let group = this.getGroup(groupKey, { notExisting: true }); // Create dummy Group to hold reference if (!group) { @@ -493,7 +493,7 @@ export class Collection { public getSelectorWithReference( selectorKey: SelectorKey ): Selector { - let selector = this.getSelector(selectorKey); + let selector = this.getSelector(selectorKey, { notExisting: true }); // Create dummy Selector to hold reference if (!selector) { @@ -578,7 +578,7 @@ export class Collection { * @param itemKey - Name/Key of Item */ public getItemWithReference(itemKey: ItemKey): Item { - let item = this.getItem(itemKey); + let item = this.getItem(itemKey, { notExisting: true }); // Create Placeholder Item to hold reference if (!item) { @@ -607,9 +607,13 @@ export class Collection { * @public * Get Value of Item by Key/Name * @param itemKey - ItemKey of Item that holds the Value + * @param config - Config */ - public getItemValue(itemKey: ItemKey | undefined): DataType | undefined { - let item = this.getItem(itemKey); + public getItemValue( + itemKey: ItemKey | undefined, + config: GetItemConfigInterface = {} + ): DataType | undefined { + let item = this.getItem(itemKey, config); if (!item) return undefined; return item.value; } @@ -756,9 +760,9 @@ export class Collection { public updateItemKey( oldItemKey: ItemKey, newItemKey: ItemKey, - config?: UpdateItemKeyConfigInterface + config: UpdateItemKeyConfigInterface = {} ): void { - const item = this.getItem(oldItemKey); + const item = this.getItem(oldItemKey, { notExisting: true }); config = defineConfig(config, { background: false, }); @@ -779,7 +783,7 @@ export class Collection { // Update Groups for (let groupKey in this.groups) { - const group = this.getGroup(groupKey); + const group = this.getGroup(groupKey, { notExisting: true }); if (!group || group.isPlaceholder || !group.has(oldItemKey)) continue; // Replace old ItemKey with new ItemKey @@ -790,7 +794,7 @@ export class Collection { // Update Selectors for (let selectorKey in this.selectors) { - const selector = this.getSelector(selectorKey); + const selector = this.getSelector(selectorKey, { notExisting: true }); if (!selector || selector.itemKey !== oldItemKey) continue; // Replace old selected ItemKey with new ItemKey @@ -821,7 +825,7 @@ export class Collection { // Remove ItemKey from Groups _groupKeys.forEach((groupKey) => { - const group = this.getGroup(groupKey); + const group = this.getGroup(groupKey, { notExisting: true }); if (!group) return; group.remove(itemKey); removedFromGroupsCount++; @@ -845,19 +849,19 @@ export class Collection { const _itemKeys = normalizeArray(itemKeys); _itemKeys.forEach((itemKey) => { - const item = this.getItem(itemKey); + const item = this.getItem(itemKey, { notExisting: true }); if (!item) return; // Remove Item from Groups for (let groupKey in this.groups) { - const group = this.getGroup(groupKey); + const group = this.getGroup(groupKey, { notExisting: true }); if (group && group.has(itemKey)) group.remove(itemKey); } // Remove Selectors that represented this Item for (let selectorKey in this.selectors) { - const selector = this.getSelector(selectorKey); - if (selector?.itemKey === itemKey) this.removeSelector(selectorKey); + const selector = this.getSelector(selectorKey, { notExisting: true }); + if (selector?._itemKey === itemKey) this.removeSelector(selectorKey); } // Remove Item from Storage @@ -903,7 +907,7 @@ export class Collection { } const itemKey = _data[primaryKey]; - let item = this.getItem(itemKey); + let item = this.getItem(itemKey, { notExisting: true }); const wasPlaceholder = item?.isPlaceholder || false; const createItem = !item; From 6fa331b7bbafea88216f34ccf35db7cf8ef308f0 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Thu, 24 Dec 2020 07:17:11 +0100 Subject: [PATCH 5/7] fixed damaged tests --- packages/core/src/collection/item.ts | 2 +- packages/core/src/collection/selector.ts | 7 +- packages/core/src/event/event.observer.ts | 10 +- packages/core/src/state/state.observer.ts | 12 +- .../core/tests/new/collection/item.test.ts | 12 +- .../tests/new/collection/selector.test.ts | 105 ++--------- .../tests/new/event/event.observer.test.ts | 43 +++-- .../{job.test.ts => runtime.job.test.ts} | 21 +-- .../core/tests/new/runtime/runtime.test.ts | 14 +- .../tests/new/state/state.observer.test.ts | 154 ++++++++++++---- packages/core/tests/new/state/state.test.ts | 170 +++--------------- 11 files changed, 229 insertions(+), 321 deletions(-) rename packages/core/tests/new/runtime/{job.test.ts => runtime.job.test.ts} (80%) diff --git a/packages/core/src/collection/item.ts b/packages/core/src/collection/item.ts index 952eb81e..115a7c75 100644 --- a/packages/core/src/collection/item.ts +++ b/packages/core/src/collection/item.ts @@ -22,7 +22,7 @@ export class Item extends State { }); this.collection = () => collection; - // Set Key to assign sideEffects + // Reassign Key to assign sideEffects this.setKey(data[collection.config.primaryKey]); } diff --git a/packages/core/src/collection/selector.ts b/packages/core/src/collection/selector.ts index d972a3c8..04aec386 100644 --- a/packages/core/src/collection/selector.ts +++ b/packages/core/src/collection/selector.ts @@ -100,12 +100,7 @@ export class Selector extends State< ); // Rebuild Selector for instantiating new 'selected' ItemKey properly - this.rebuildSelector({ - background: config.background, - sideEffects: config.sideEffects, - force: config.force, - overwrite: config.overwrite, - }); + this.rebuildSelector(config); return this; } diff --git a/packages/core/src/event/event.observer.ts b/packages/core/src/event/event.observer.ts index aa750fcc..0684d2e5 100644 --- a/packages/core/src/event/event.observer.ts +++ b/packages/core/src/event/event.observer.ts @@ -7,6 +7,7 @@ import { IngestConfigInterface, RuntimeJobConfigInterface, defineConfig, + RuntimeJobKey, } from "../internal"; export class EventObserver extends Observer { @@ -51,7 +52,7 @@ export class EventObserver extends Observer { force: config.force, sideEffects: config.sideEffects, background: config.background, - key: this._key, + key: config.key || this._key, }); this.agileInstance().runtime.ingest(job, { @@ -83,6 +84,11 @@ export interface CreateEventObserverConfigInterface { key?: ObserverKey; } +/** + * @param key - Key/Name of Job that gets created + */ export interface EventIngestConfigInterface extends RuntimeJobConfigInterface, - IngestConfigInterface {} + IngestConfigInterface { + key?: RuntimeJobKey; +} diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 12d52154..4932cef6 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -12,6 +12,7 @@ import { IngestConfigInterface, StateRuntimeJob, StateRuntimeJobConfigInterface, + RuntimeJobKey, } from "../internal"; export class StateObserver extends Observer { @@ -74,7 +75,7 @@ export class StateObserver extends Observer { overwrite: false, }); - // Force overwriting State because if setting Value the State shouldn't be a placeholder anymore + // Force overwriting State because if assigning Value to State, the State shouldn't be a placeholder anymore if (state.isPlaceholder) { config.force = true; config.overwrite = true; @@ -95,7 +96,7 @@ export class StateObserver extends Observer { force: config.force, background: config.background, overwrite: config.overwrite, - key: this._key, + key: config.key || this._key, }); this.agileInstance().runtime.ingest(job, { @@ -173,6 +174,11 @@ export interface CreateStateObserverConfigInterface { key?: ObserverKey; } +/** + * @param key - Key/Name of Job that gets created + */ export interface StateIngestConfigInterface extends StateRuntimeJobConfigInterface, - IngestConfigInterface {} + IngestConfigInterface { + key?: RuntimeJobKey; +} diff --git a/packages/core/tests/new/collection/item.test.ts b/packages/core/tests/new/collection/item.test.ts index 39a779e8..89b01d14 100644 --- a/packages/core/tests/new/collection/item.test.ts +++ b/packages/core/tests/new/collection/item.test.ts @@ -24,7 +24,7 @@ describe("Item Tests", () => { dummyData[dummyCollection.config.primaryKey] ); - expect(item._key).toBeUndefined(); + expect(item._key).toBe(dummyData[dummyCollection.config.primaryKey]); expect(item.valueType).toBeUndefined(); expect(item.isSet).toBeFalsy(); expect(item.isPlaceholder).toBeFalsy(); @@ -34,7 +34,9 @@ describe("Item Tests", () => { expect(item.nextStateValue).toStrictEqual(dummyData); expect(item.observer).toBeInstanceOf(StateObserver); expect(item.observer.deps.size).toBe(0); - expect(item.observer._key).toBeUndefined(); + expect(item.observer._key).toBe( + dummyData[dummyCollection.config.primaryKey] + ); expect(item.sideEffects).toStrictEqual({}); expect(item.computeMethod).toBeUndefined(); expect(item.isPersisted).toBeFalsy(); @@ -56,7 +58,7 @@ describe("Item Tests", () => { dummyData[dummyCollection.config.primaryKey] ); - expect(item._key).toBeUndefined(); + expect(item._key).toBe(dummyData[dummyCollection.config.primaryKey]); expect(item.valueType).toBeUndefined(); expect(item.isSet).toBeFalsy(); expect(item.isPlaceholder).toBeTruthy(); @@ -66,7 +68,9 @@ describe("Item Tests", () => { expect(item.nextStateValue).toStrictEqual(dummyData); expect(item.observer).toBeInstanceOf(StateObserver); expect(item.observer.deps.size).toBe(0); - expect(item.observer._key).toBeUndefined(); + expect(item.observer._key).toBe( + dummyData[dummyCollection.config.primaryKey] + ); expect(item.sideEffects).toStrictEqual({}); expect(item.computeMethod).toBeUndefined(); expect(item.isPersisted).toBeFalsy(); diff --git a/packages/core/tests/new/collection/selector.test.ts b/packages/core/tests/new/collection/selector.test.ts index 8368d1b0..810bb108 100644 --- a/packages/core/tests/new/collection/selector.test.ts +++ b/packages/core/tests/new/collection/selector.test.ts @@ -173,19 +173,14 @@ describe("Selector Tests", () => { background: false, sideEffects: true, force: false, + overwrite: false, + storage: true, }); expect(dummyCollection.data).toHaveProperty("dummyItem1Key"); expect(dummyCollection.data["dummyItem1Key"]).toBe(dummyItem1); expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem2._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem2._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem1._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem1._value); - expect(selector.isPlaceholder).toBeFalsy(); - expect(selector.isSet).toBeTruthy(); }); it("should unselect old selected Item and select new Item (specific config)", () => { @@ -195,6 +190,7 @@ describe("Selector Tests", () => { force: true, sideEffects: false, background: true, + overwrite: true, }); expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( @@ -213,19 +209,14 @@ describe("Selector Tests", () => { background: true, sideEffects: false, force: true, + overwrite: true, + storage: true, }); expect(dummyCollection.data).toHaveProperty("dummyItem1Key"); expect(dummyCollection.data["dummyItem1Key"]).toBe(dummyItem1); expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem2._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem2._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem1._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem1._value); - expect(selector.isPlaceholder).toBeFalsy(); - expect(selector.isSet).toBeTruthy(); }); it("should print warning if trying to select selected Item again (default config)", () => { @@ -250,13 +241,6 @@ describe("Selector Tests", () => { expect(dummyCollection.data["dummyItem1Key"]).toBe(dummyItem1); expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem1._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem1._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem1._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem1._value); - expect(selector.isPlaceholder).toBeFalsy(); - expect(selector.isSet).toBeFalsy(); }); it("should be able to select selected Item again (config.force = true)", () => { @@ -282,54 +266,14 @@ describe("Selector Tests", () => { background: false, sideEffects: true, force: true, + overwrite: false, + storage: true, }); expect(dummyCollection.data).toHaveProperty("dummyItem1Key"); expect(dummyCollection.data["dummyItem1Key"]).toBe(dummyItem1); expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem1._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem1._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem1._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem1._value); - expect(selector.isSet).toBeFalsy(); - }); - - it("should unselect old selected Item and select new Item with overwriting Selector (config.overwrite = true)", () => { - dummyCollection.getItemWithReference = jest.fn(() => dummyItem2); - - selector.select("dummyItem2Key", { overwrite: true }); - - expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( - "dummyItem2Key" - ); - expect(selector._itemKey).toBe("dummyItem2Key"); - expect(selector.item).toBe(dummyItem2); - expect(dummyItem1.removeSideEffect).toHaveBeenCalledWith( - Selector.rebuildSelectorSideEffectKey - ); - expect(dummyItem2.addSideEffect).toHaveBeenCalledWith( - Selector.rebuildSelectorSideEffectKey, - expect.any(Function) - ); - expect(selector.rebuildSelector).toHaveBeenCalledWith({ - background: false, - sideEffects: true, - force: true, - }); - - expect(dummyCollection.data).toHaveProperty("dummyItem1Key"); - expect(dummyCollection.data["dummyItem1Key"]).toBe(dummyItem1); - expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); - expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem2._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem2._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem2._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem2._value); - expect(selector.isPlaceholder).toBeFalsy(); - expect(selector.isSet).toBeFalsy(); }); it("should remove old selected Item, select new Item and overwrite Selector if old Item is placeholder (default config)", async () => { @@ -353,19 +297,14 @@ describe("Selector Tests", () => { expect(selector.rebuildSelector).toHaveBeenCalledWith({ background: false, sideEffects: true, - force: true, + force: false, + overwrite: true, + storage: true, }); expect(dummyCollection.data).not.toHaveProperty("dummyItem1Key"); expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem2._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem2._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem2._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem2._value); - expect(selector.isPlaceholder).toBeFalsy(); - expect(selector.isSet).toBeFalsy(); }); it("should remove old selected Item, select new Item and shouldn't overwrite Selector if old Item is placeholder (config.overwrite = false)", async () => { @@ -390,18 +329,13 @@ describe("Selector Tests", () => { background: false, sideEffects: true, force: false, + overwrite: false, + storage: true, }); expect(dummyCollection.data).not.toHaveProperty("dummyItem1Key"); expect(dummyCollection.data).toHaveProperty("dummyItem2Key"); expect(dummyCollection.data["dummyItem2Key"]).toBe(dummyItem2); - - expect(selector._value).toStrictEqual(dummyItem2._value); - expect(selector.nextStateValue).toStrictEqual(dummyItem2._value); - expect(selector.previousStateValue).toStrictEqual(dummyItem1._value); - expect(selector.initialStateValue).toStrictEqual(dummyItem1._value); - expect(selector.isPlaceholder).toBeFalsy(); - expect(selector.isSet).toBeTruthy(); }); describe("test added sideEffect called Selector.rebuildSelectorSideEffectKey", () => { @@ -433,12 +367,7 @@ describe("Selector Tests", () => { selector.rebuildSelector(); - expect(selector.set).toHaveBeenCalledWith(selector.item._value, { - sideEffects: true, - background: false, - force: false, - storage: true, - }); + expect(selector.set).toHaveBeenCalledWith(selector.item._value, {}); }); it("should set selector value to item value (specific config)", () => { @@ -454,7 +383,6 @@ describe("Selector Tests", () => { sideEffects: false, background: true, force: true, - storage: true, }); }); @@ -463,12 +391,7 @@ describe("Selector Tests", () => { selector.rebuildSelector(); - expect(selector.set).toHaveBeenCalledWith(undefined, { - sideEffects: true, - background: false, - force: false, - storage: true, - }); + expect(selector.set).toHaveBeenCalledWith(undefined, {}); }); }); }); diff --git a/packages/core/tests/new/event/event.observer.test.ts b/packages/core/tests/new/event/event.observer.test.ts index 83ceebe7..e77ec824 100644 --- a/packages/core/tests/new/event/event.observer.test.ts +++ b/packages/core/tests/new/event/event.observer.test.ts @@ -4,6 +4,7 @@ import { Observer, SubscriptionContainer, Event, + RuntimeJob, } from "../../../src"; describe("EventObserver Tests", () => { @@ -60,31 +61,51 @@ describe("EventObserver Tests", () => { }); describe("trigger function tests", () => { - it("should ingest Event into Runtime (default config)", () => { - dummyAgile.runtime.ingest = jest.fn(); + it("should create RuntimeJob and ingest it into the Runtime (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: RuntimeJob) => { + expect(job._key).toBe(eventObserver._key); + expect(job.observer).toBe(eventObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + }); + }); eventObserver.trigger(); expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( - eventObserver, - {} + expect.any(RuntimeJob), + { + perform: true, + } ); }); it("should ingest Event into Runtime (specific config)", () => { - dummyAgile.runtime.ingest = jest.fn(); + dummyAgile.runtime.ingest = jest.fn((job: RuntimeJob) => { + expect(job._key).toBe("coolKey"); + expect(job.observer).toBe(eventObserver); + expect(job.config).toStrictEqual({ + background: true, + sideEffects: true, + force: true, + }); + }); eventObserver.trigger({ background: true, key: "coolKey", - storage: false, + perform: false, + force: true, }); - expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith(eventObserver, { - background: true, - key: "coolKey", - storage: false, - }); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(RuntimeJob), + { + perform: false, + } + ); }); }); diff --git a/packages/core/tests/new/runtime/job.test.ts b/packages/core/tests/new/runtime/runtime.job.test.ts similarity index 80% rename from packages/core/tests/new/runtime/job.test.ts rename to packages/core/tests/new/runtime/runtime.job.test.ts index 124dcda8..77d0f4ef 100644 --- a/packages/core/tests/new/runtime/job.test.ts +++ b/packages/core/tests/new/runtime/runtime.job.test.ts @@ -1,6 +1,6 @@ import { Agile, Integration, RuntimeJob, Observer } from "../../../src"; -describe("Job Tests", () => { +describe("RuntimeJob Tests", () => { let dummyAgile: Agile; let dummyIntegration: Integration; let dummyObserver: Observer; @@ -13,7 +13,7 @@ describe("Job Tests", () => { dummyObserver = new Observer(dummyAgile); }); - it("should create Job with Agile that has integrations (default config)", () => { + it("should create RuntimeJob with Agile that has integrations (default config)", () => { dummyAgile.integrate(dummyIntegration); const job = new RuntimeJob(dummyObserver); @@ -24,21 +24,19 @@ describe("Job Tests", () => { background: false, sideEffects: true, force: false, - storage: true, }); expect(job.rerender).toBeTruthy(); expect(job.performed).toBeFalsy(); expect(job.subscriptionContainersToUpdate.size).toBe(0); }); - it("should create Job with Agile that has integrations (specific config)", () => { + it("should create RuntimeJob with Agile that has integrations (specific config)", () => { dummyAgile.integrate(dummyIntegration); const job = new RuntimeJob(dummyObserver, { key: "dummyJob", sideEffects: false, force: true, - storage: false, }); expect(job._key).toBe("dummyJob"); @@ -47,14 +45,13 @@ describe("Job Tests", () => { background: false, sideEffects: false, force: true, - storage: false, }); expect(job.rerender).toBeTruthy(); expect(job.performed).toBeFalsy(); expect(job.subscriptionContainersToUpdate.size).toBe(0); }); - it("should create Job with Agile that has no integrations (default config)", () => { + it("should create RuntimeJob with Agile that has no integrations (default config)", () => { const job = new RuntimeJob(dummyObserver); expect(job._key).toBeUndefined(); @@ -63,14 +60,13 @@ describe("Job Tests", () => { background: false, sideEffects: true, force: false, - storage: true, }); expect(job.rerender).toBeFalsy(); expect(job.performed).toBeFalsy(); expect(job.subscriptionContainersToUpdate.size).toBe(0); }); - it("should create Job and Agile that has integrations (config.background = true)", () => { + it("should create RuntimeJob and Agile that has integrations (config.background = true)", () => { dummyAgile.integrate(dummyIntegration); const job = new RuntimeJob(dummyObserver, { background: true }); @@ -81,14 +77,13 @@ describe("Job Tests", () => { background: true, sideEffects: true, force: false, - storage: true, }); expect(job.rerender).toBeFalsy(); expect(job.performed).toBeFalsy(); expect(job.subscriptionContainersToUpdate.size).toBe(0); }); - describe("Job Function Tests", () => { + describe("RuntimeJob Function Tests", () => { let job: RuntimeJob; beforeEach(() => { @@ -96,7 +91,7 @@ describe("Job Tests", () => { }); describe("key get function tests", () => { - it("should return key of Job", () => { + it("should return key of RuntimeJob", () => { job._key = "myCoolKey"; expect(job.key).toBe("myCoolKey"); @@ -104,7 +99,7 @@ describe("Job Tests", () => { }); describe("key set function tests", () => { - it("should update key in Job", () => { + it("should update key in RuntimeJob", () => { job.key = "myCoolKey"; expect(job._key).toBe("myCoolKey"); diff --git a/packages/core/tests/new/runtime/runtime.test.ts b/packages/core/tests/new/runtime/runtime.test.ts index f50b04da..bc9d3c67 100644 --- a/packages/core/tests/new/runtime/runtime.test.ts +++ b/packages/core/tests/new/runtime/runtime.test.ts @@ -47,24 +47,20 @@ describe("Runtime Tests", () => { dummyJob = new RuntimeJob(dummyObserver1); runtime.perform = jest.fn(); - runtime.jobQueue.shift = jest.fn(() => dummyJob); }); it("should create Job and perform it (default config)", () => { - runtime.ingest(dummyObserver1, { key: "coolJob" }); + runtime.ingest(dummyJob); - expect(runtime.jobQueue.length).toBe(1); - expect(runtime.jobQueue[0]._key).toBe("coolJob"); - expect(runtime.jobQueue.shift).toHaveBeenCalled(); - expect(runtime.perform).toHaveBeenCalledWith(dummyJob); // Dummy Job because of mocking jobQueue.shift + expect(runtime.jobQueue.length).toBe(0); + expect(runtime.perform).toHaveBeenCalledWith(dummyJob); }); it("should create Job and shouldn't perform it (config.perform = false)", () => { - runtime.ingest(dummyObserver1, { perform: false, key: "coolJob" }); + runtime.ingest(dummyJob, { perform: false }); expect(runtime.jobQueue.length).toBe(1); - expect(runtime.jobQueue[0]._key).toBe("coolJob"); - expect(runtime.jobQueue.shift).not.toHaveBeenCalled(); + expect(runtime.jobQueue[0]).toBe(dummyJob); expect(runtime.perform).not.toHaveBeenCalled(); }); }); diff --git a/packages/core/tests/new/state/state.observer.test.ts b/packages/core/tests/new/state/state.observer.test.ts index 7f3ab2ed..435967c0 100644 --- a/packages/core/tests/new/state/state.observer.test.ts +++ b/packages/core/tests/new/state/state.observer.test.ts @@ -1,7 +1,7 @@ import { Agile, Computed, - RuntimeJob, + StateRuntimeJob, Observer, State, StateObserver, @@ -110,7 +110,7 @@ describe("StateObserver Tests", () => { }); }); - it("should call ingestValue with computedValue if observer belongs to a ComputedState (default config)", () => { + it("should call ingestValue with computedValue if Observer belongs to a ComputedState (default config)", () => { dummyComputed.computeValue = jest.fn(() => "computedValue"); computedObserver.ingest(); @@ -129,16 +129,57 @@ describe("StateObserver Tests", () => { }); it("should ingest State into Runtime if newValue isn't equal to currentValue (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + }); + stateObserver.ingestValue("updatedDummyValue"); expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); - expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith(stateObserver, { - perform: true, - background: false, - sideEffects: true, - force: false, - storage: true, + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); + }); + + it("should ingest State into Runtime if newValue isn't equal to currentValue (specific config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe("dummyJob"); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: false, + force: true, + storage: true, + overwrite: true, + }); }); + + stateObserver.ingestValue("updatedDummyValue", { + perform: false, + force: true, + sideEffects: false, + overwrite: true, + key: "dummyJob", + }); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: false, + } + ); }); it("shouldn't ingest State into Runtime if newValue is equal to currentValue (default config)", () => { @@ -152,40 +193,87 @@ describe("StateObserver Tests", () => { it("should ingest State into Runtime if newValue is equal to currentValue (config.force = true)", () => { dummyState._value = "updatedDummyValue"; + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: true, + storage: true, + overwrite: false, + }); + }); + stateObserver.ingestValue("updatedDummyValue", { force: true }); expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); - expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith(stateObserver, { - perform: true, - background: false, - sideEffects: true, - force: true, - storage: true, + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); + }); + + it("should ingest placeholder State into Runtime if newValue isn't equal to currentValue (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: true, + storage: true, + overwrite: true, + }); }); + dummyState.isPlaceholder = true; + + stateObserver.ingestValue("updatedDummyValue"); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); }); it("should ingest State into Runtime and compute newStateValue if State compute Function is set (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + }); dummyState.computeMethod = (value) => `cool value '${value}'`; + stateObserver.ingestValue("updatedDummyValue"); expect(stateObserver.nextStateValue).toBe( "cool value 'updatedDummyValue'" ); - expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith(stateObserver, { - perform: true, - background: false, - sideEffects: true, - force: false, - storage: true, - }); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); }); }); describe("perform function tests", () => { - let dummyJob: RuntimeJob; + let dummyJob: StateRuntimeJob; beforeEach(() => { - dummyJob = new RuntimeJob(stateObserver, { + dummyJob = new StateRuntimeJob(stateObserver, { key: "dummyJob", }); dummyState.persistent = new StatePersistent(dummyState); @@ -199,7 +287,7 @@ describe("StateObserver Tests", () => { dummyState.initialStateValue = "initialValue"; dummyState._value = "dummyValue"; - stateObserver.perform(dummyJob as any); + stateObserver.perform(dummyJob); expect(dummyState.previousStateValue).toBe("dummyValue"); expect(dummyState.initialStateValue).toBe("initialValue"); @@ -210,19 +298,21 @@ describe("StateObserver Tests", () => { expect(stateObserver.sideEffects).toHaveBeenCalledWith(dummyJob); }); - it("should perform Job and assign specific values to State if State is a Placeholder", () => { + it("should perform Job and overwrite State (job.config.overwrite = true)", () => { dummyJob.observer.nextStateValue = "newValue"; + dummyJob.config.overwrite = true; + dummyState.isPlaceholder = true; dummyState.initialStateValue = "overwriteValue"; dummyState._value = "dummyValue"; - dummyState.isPlaceholder = true; - stateObserver.perform(dummyJob as any); + stateObserver.perform(dummyJob); expect(dummyState.previousStateValue).toBe("newValue"); expect(dummyState.initialStateValue).toBe("newValue"); expect(dummyState._value).toBe("newValue"); expect(dummyState.nextStateValue).toBe("newValue"); - expect(dummyState.isSet).toBeTruthy(); + expect(dummyState.isSet).toBeFalsy(); + expect(dummyState.isPlaceholder).toBeFalsy(); expect(stateObserver.value).toBe("newValue"); expect(stateObserver.sideEffects).toHaveBeenCalledWith(dummyJob); }); @@ -232,7 +322,7 @@ describe("StateObserver Tests", () => { dummyState.initialStateValue = "newValue"; dummyState._value = "dummyValue"; - stateObserver.perform(dummyJob as any); + stateObserver.perform(dummyJob); expect(dummyState.previousStateValue).toBe("dummyValue"); expect(dummyState.initialStateValue).toBe("newValue"); @@ -245,12 +335,12 @@ describe("StateObserver Tests", () => { }); describe("sideEffects function tests", () => { - let dummyJob: RuntimeJob; + let dummyJob: StateRuntimeJob; let dummyStateObserver: StateObserver; beforeEach(() => { dummyStateObserver = new StateObserver(new State(dummyAgile, "test")); - dummyJob = new RuntimeJob(stateObserver, { + dummyJob = new StateRuntimeJob(stateObserver, { key: "dummyJob", }); @@ -276,7 +366,7 @@ describe("StateObserver Tests", () => { }); }); - it("shouldn't call sideEffects of State(job.config.sideEffects = false)", () => { + it("should call watchers, ingest dependencies of State and shouldn't call sideEffects (job.config.sideEffects = false)", () => { dummyState._value = "dummyValue"; dummyJob.config.sideEffects = false; stateObserver.sideEffects(dummyJob); diff --git a/packages/core/tests/new/state/state.test.ts b/packages/core/tests/new/state/state.test.ts index 49c58416..1d7e3827 100644 --- a/packages/core/tests/new/state/state.test.ts +++ b/packages/core/tests/new/state/state.test.ts @@ -214,7 +214,7 @@ describe("State Tests", () => { jest.spyOn(numberState.observer, "ingestValue"); }); - it("should update value of State if value has correct type (default config)", () => { + it("should ingestValue if value has correct type (default config)", () => { numberState.set(20); expect(console.warn).not.toHaveBeenCalled(); @@ -224,17 +224,11 @@ describe("State Tests", () => { background: false, force: false, storage: true, + overwrite: false, }); - - expect(numberState._value).toBe(20); - expect(numberState.nextStateValue).toBe(20); - expect(numberState.previousStateValue).toBe(10); - expect(numberState.initialStateValue).toBe(10); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeTruthy(); }); - it("should update value of State if value has correct type (specific config)", () => { + it("should ingestValue if value has correct type (specific config)", () => { numberState.set(20, { sideEffects: false, background: true, @@ -248,17 +242,11 @@ describe("State Tests", () => { background: true, force: false, storage: false, + overwrite: false, }); - - expect(numberState._value).toBe(20); - expect(numberState.nextStateValue).toBe(20); - expect(numberState.previousStateValue).toBe(10); - expect(numberState.initialStateValue).toBe(10); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeTruthy(); }); - it("shouldn't update value of State if value hasn't correct type (default config)", () => { + it("shouldn't ingestValue if value hasn't correct type (default config)", () => { numberState.type(Number); numberState.set("coolValue" as any); @@ -268,16 +256,9 @@ describe("State Tests", () => { "Agile Error: Incorrect type (string) was provided." ); expect(numberState.observer.ingestValue).not.toHaveBeenCalled(); - - expect(numberState._value).toBe(10); - expect(numberState.nextStateValue).toBe(10); - expect(numberState.previousStateValue).toBe(10); - expect(numberState.initialStateValue).toBe(10); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeFalsy(); }); - it("should update value of State if value hasn't correct type (config.force = true)", () => { + it("should ingestValue if value hasn't correct type (config.force = true)", () => { numberState.type(Number); numberState.set("coolValue" as any, { force: true }); @@ -293,18 +274,12 @@ describe("State Tests", () => { background: false, force: true, storage: true, + overwrite: false, } ); - - expect(numberState._value).toBe("coolValue"); - expect(numberState.nextStateValue).toBe("coolValue"); - expect(numberState.previousStateValue).toBe(10); - expect(numberState.initialStateValue).toBe(10); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeTruthy(); }); - it("should update value of State if value hasn't correct type but the type isn't explicit defined (default config)", () => { + it("should ingestValue if value hasn't correct type but the type isn't explicit defined (default config)", () => { numberState.set("coolValue" as any); expect(console.warn).not.toHaveBeenCalled(); @@ -316,79 +291,9 @@ describe("State Tests", () => { background: false, force: false, storage: true, + overwrite: false, } ); - - expect(numberState._value).toBe("coolValue"); - expect(numberState.nextStateValue).toBe("coolValue"); - expect(numberState.previousStateValue).toBe(10); - expect(numberState.initialStateValue).toBe(10); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeTruthy(); - }); - - it("should update values of State and overwrite it (config.overwrite = true)", () => { - numberState.set(20, { overwrite: true }); - - expect(console.warn).not.toHaveBeenCalled(); - expect(console.error).not.toHaveBeenCalled(); - expect(numberState.observer.ingestValue).toHaveBeenCalledWith(20, { - sideEffects: true, - background: false, - force: true, - storage: true, - }); - - expect(numberState._value).toBe(20); - expect(numberState.nextStateValue).toBe(20); - expect(numberState.previousStateValue).toBe(20); - expect(numberState.initialStateValue).toBe(20); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeFalsy(); - }); - - it("should update values of State and overwrite it if State is placeholder (default config)", () => { - numberState.isPlaceholder = true; - - numberState.set(20); - - expect(console.warn).not.toHaveBeenCalled(); - expect(console.error).not.toHaveBeenCalled(); - expect(numberState.observer.ingestValue).toHaveBeenCalledWith(20, { - sideEffects: true, - background: false, - force: true, - storage: true, - }); - - expect(numberState._value).toBe(20); - expect(numberState.nextStateValue).toBe(20); - expect(numberState.previousStateValue).toBe(20); - expect(numberState.initialStateValue).toBe(20); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeFalsy(); - }); - - it("should update values of State and shouldn't overwrite it if State is placeholder (config.overwrite = false)", () => { - numberState.isPlaceholder = true; - - numberState.set(20, { overwrite: false }); - - expect(console.warn).not.toHaveBeenCalled(); - expect(console.error).not.toHaveBeenCalled(); - expect(numberState.observer.ingestValue).toHaveBeenCalledWith(20, { - sideEffects: true, - background: false, - force: false, - storage: true, - }); - - expect(numberState._value).toBe(20); - expect(numberState.nextStateValue).toBe(20); - expect(numberState.previousStateValue).toBe(10); - expect(numberState.initialStateValue).toBe(10); - expect(numberState.isPlaceholder).toBeFalsy(); - expect(numberState.isSet).toBeTruthy(); }); }); @@ -397,24 +302,19 @@ describe("State Tests", () => { numberState.observer.ingest = jest.fn(); }); - it("should call ingest function in the observer (default config)", () => { + it("should call ingest function in Observer (default config)", () => { numberState.ingest(); - expect(numberState.observer.ingest).toHaveBeenCalledWith({ - sideEffects: true, - background: false, - force: false, - }); + expect(numberState.observer.ingest).toHaveBeenCalledWith({}); }); - it("should call ingest function in the observer (specific config)", () => { + it("should call ingest function in Observer (specific config)", () => { numberState.ingest({ force: true, background: true, }); expect(numberState.observer.ingest).toHaveBeenCalledWith({ - sideEffects: true, background: true, force: true, }); @@ -548,6 +448,9 @@ describe("State Tests", () => { expect(objectState.ingest).toHaveBeenCalledWith({ background: false, force: false, + overwrite: false, + sideEffects: true, + storage: true, }); }); @@ -557,6 +460,9 @@ describe("State Tests", () => { { addNewProperties: false, background: true, + force: true, + overwrite: true, + sideEffects: false, } ); @@ -573,44 +479,10 @@ describe("State Tests", () => { }); expect(objectState.ingest).toHaveBeenCalledWith({ background: true, - force: false, - }); - }); - - it("should patch and shouldn't ingest passed object based value into a object based State if patch result is equal to currentValue (default config)", () => { - objectState.patch({ name: "jeff" }); - - expect(Utils.flatMerge).toHaveBeenCalledWith( - { age: 10, name: "jeff" }, - { name: "jeff" }, - { - addNewProperties: true, - } - ); - expect(objectState.nextStateValue).toStrictEqual({ - age: 10, - name: "jeff", - }); - expect(objectState.ingest).not.toHaveBeenCalled(); - }); - - it("should patch and ingest passed object based value into a object based State if patch result is equal to currentValue (config.force = true)", () => { - objectState.patch({ name: "jeff" }, { force: true }); - - expect(Utils.flatMerge).toHaveBeenCalledWith( - { age: 10, name: "jeff" }, - { name: "jeff" }, - { - addNewProperties: true, - } - ); - expect(objectState.nextStateValue).toStrictEqual({ - age: 10, - name: "jeff", - }); - expect(objectState.ingest).toHaveBeenCalledWith({ - background: false, force: true, + overwrite: true, + sideEffects: false, + storage: true, }); }); }); From 6b89fda0d10c19e260c9fc179d78b4f7ed240867 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Thu, 24 Dec 2020 07:33:42 +0100 Subject: [PATCH 6/7] fixed typo --- packages/core/tests/new/runtime/runtime.test.ts | 4 ++-- .../core/tests/new/state/state.observer.test.ts | 13 +------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/core/tests/new/runtime/runtime.test.ts b/packages/core/tests/new/runtime/runtime.test.ts index bc9d3c67..16fcedce 100644 --- a/packages/core/tests/new/runtime/runtime.test.ts +++ b/packages/core/tests/new/runtime/runtime.test.ts @@ -49,14 +49,14 @@ describe("Runtime Tests", () => { runtime.perform = jest.fn(); }); - it("should create Job and perform it (default config)", () => { + it("should perform passed Job (default config)", () => { runtime.ingest(dummyJob); expect(runtime.jobQueue.length).toBe(0); expect(runtime.perform).toHaveBeenCalledWith(dummyJob); }); - it("should create Job and shouldn't perform it (config.perform = false)", () => { + it("shouldn't perform passed Job (config.perform = false)", () => { runtime.ingest(dummyJob, { perform: false }); expect(runtime.jobQueue.length).toBe(1); diff --git a/packages/core/tests/new/state/state.observer.test.ts b/packages/core/tests/new/state/state.observer.test.ts index 435967c0..a8cff5d9 100644 --- a/packages/core/tests/new/state/state.observer.test.ts +++ b/packages/core/tests/new/state/state.observer.test.ts @@ -216,7 +216,7 @@ describe("StateObserver Tests", () => { ); }); - it("should ingest placeholder State into Runtime if newValue isn't equal to currentValue (default config)", () => { + it("should ingest placeholder State into Runtime (default config)", () => { dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { expect(job._key).toBe(stateObserver._key); expect(job.observer).toBe(stateObserver); @@ -242,17 +242,6 @@ describe("StateObserver Tests", () => { }); it("should ingest State into Runtime and compute newStateValue if State compute Function is set (default config)", () => { - dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { - expect(job._key).toBe(stateObserver._key); - expect(job.observer).toBe(stateObserver); - expect(job.config).toStrictEqual({ - background: false, - sideEffects: true, - force: false, - storage: true, - overwrite: false, - }); - }); dummyState.computeMethod = (value) => `cool value '${value}'`; stateObserver.ingestValue("updatedDummyValue"); From bfe468c429d495da5bebd3c01f92af627aca6384 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Thu, 24 Dec 2020 07:47:44 +0100 Subject: [PATCH 7/7] created StateRuntimeJob Tests --- .../tests/new/state/state.runtime.job.test.ts | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 packages/core/tests/new/state/state.runtime.job.test.ts diff --git a/packages/core/tests/new/state/state.runtime.job.test.ts b/packages/core/tests/new/state/state.runtime.job.test.ts new file mode 100644 index 00000000..9e57bf58 --- /dev/null +++ b/packages/core/tests/new/state/state.runtime.job.test.ts @@ -0,0 +1,104 @@ +import { + Agile, + RuntimeJob, + StateObserver, + StateRuntimeJob, + State, + Integration, +} from "../../../src"; + +// jest.mock("../../../src/runtime/runtime.job"); // Can't mock RuntimeJob because mocks get instantiated before everything else -> I got the good old not loaded Object error https://github.com/kentcdodds/how-jest-mocking-works + +describe("RuntimeJob Tests", () => { + let dummyAgile: Agile; + let dummyIntegration: Integration; + let dummyState: State; + let dummyObserver: StateObserver; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + dummyIntegration = new Integration({ + key: "myIntegration", + }); + dummyState = new State(dummyAgile, "dummyValue"); + dummyObserver = new StateObserver(dummyState); + }); + + it("should create RuntimeJob with Agile that has integrations (default config)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new StateRuntimeJob(dummyObserver); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeTruthy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob with Agile that has integrations (specific config)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new StateRuntimeJob(dummyObserver, { + key: "dummyJob", + sideEffects: false, + force: true, + }); + + expect(job._key).toBe("dummyJob"); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: false, + force: true, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeTruthy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob with Agile that has no integrations (default config)", () => { + const job = new StateRuntimeJob(dummyObserver); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeFalsy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob and Agile that has integrations (config.background = true)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new StateRuntimeJob(dummyObserver, { background: true }); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: true, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeFalsy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); +});