Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

split hydration on DOM and Server + add several tests #141

Merged
merged 5 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ __tests__/async-state

.scannerwork
memlab
packages/demo
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"author": "incepter",
"sideEffects": false,
"name": "async-states",
"version": "1.1.1",
"version": "1.2.0",
"main": "dist/umd/index",
"types": "dist/es/index",
"module": "dist/es/src/index",
Expand Down
57 changes: 57 additions & 0 deletions packages/core/src/__tests__/async-state/AsyncState.context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {Sources} from "../../AsyncState";
import {createContext, terminateContext} from "../../pool";

describe('Create instances in different contexts', () => {
let consoleErrorSpy;
let originalConsoleError = console.error;
beforeAll(() => {
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
})
afterAll(() => {
console.error = originalConsoleError
})

it('should reuse the same instance when no context is provided', () => {
consoleErrorSpy.mockClear()
let source1 = Sources.for("key1")
let source2 = Sources.for("key1")
expect(source1).toBe(source2)
expect(consoleErrorSpy).toHaveBeenCalledTimes(1)
});

it('should reuse the same instance in the same pool', () => {
let ctx = {}
createContext(ctx)
let source1 = Sources.for("key2", null, {context: ctx})
let source2 = Sources.for("key2", null, {context: ctx})
expect(source1).toBe(source2)
});

it('should use a different instance from different pools', () => {
let ctx1 = {}
let ctx2 = {}
createContext(ctx1)
createContext(ctx2)
let source1 = Sources.for("key3", null, {context: ctx1})
let source2 = Sources.for("key3", null, {context: ctx2})
expect(source1).not.toBe(source2)
});
it('should throw when context isnt created first', () => {
let ctx1 = {}
expect(
() => Sources.for("key4", null, {context: ctx1})
).toThrow("No execution context for context [object Object]")
});
it('should throw when context is already terminated', () => {
let ctx = {}

createContext(ctx)
expect(() => Sources.for("key5", null, {context: ctx}))
.not
.toThrow("No execution context for context [object Object]")

terminateContext(ctx)
expect(() => Sources.for("key6", null, {context: ctx}))
.toThrow("No execution context for context [object Object]")
});
});
98 changes: 98 additions & 0 deletions packages/core/src/__tests__/async-state/AsyncState.pools.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {AsyncState, Sources} from "../../AsyncState";
import {requestContext} from "../../pool";
import {flushPromises} from "../utils/test-utils";

describe('Create instances in different pools', () => {
let consoleErrorSpy;
let originalConsoleError = console.error;
beforeAll(() => {
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
})
afterAll(() => {
console.error = originalConsoleError
})

it('should reuse the same instance when no pool is provided', () => {
consoleErrorSpy.mockClear()
let source1 = Sources.for("key1")
let source2 = Sources.for("key1")
let source3 = Sources.for("another1")
expect(source1).toBe(source2)
expect(source1).not.toBe(source3)
expect(source2).not.toBe(source3)
expect(consoleErrorSpy).toHaveBeenCalledTimes(1)
});

it('should reuse the same instance in the same pool', () => {
consoleErrorSpy.mockClear()
let source1 = Sources.for("key2", null, {pool: "pool-1"})
let source2 = Sources.for("key2", null, {pool: "pool-1"})
expect(source1).toBe(source2)
});

it('should use a different instance from different pools', () => {
consoleErrorSpy.mockClear()
let source1 = Sources.for("key3", null, {pool: "pool-1"})
let source2 = Sources.for("key3", null, {pool: "pool-2"})
expect(source1).not.toBe(source2)
});

it('should merge payload inside all pools instances', () => {
let testPool = requestContext(null).getOrCreatePool("test-pool")

// @ts-ignore
testPool.set(null, {})
expect(testPool.instances.size).toBe(0) // nothing was added


let src = Sources.for("test-key", null, {pool: "test-pool"})
let src2 = Sources.for("test-key2", null, {pool: "test-pool"})
expect(src.getPayload()).toEqual({})
expect(src.getPayload()).toEqual({})
expect(testPool.instances.size).toBe(2)

testPool.mergePayload({hello: true})
expect(src.getPayload()).toEqual({hello: true})

src.mergePayload({ok: true})
testPool.mergePayload({ok2: true})
expect(src.getPayload()).toEqual({hello: true, ok: true, ok2: true})
expect(src2.getPayload()).toEqual({hello: true, ok2: true})
});

it('should listen to instances being added to pool', async () => {
let testPool = requestContext(null).getOrCreatePool("test-pool")

let handler = jest.fn()
testPool.listen(handler)

let src = new AsyncState("test-key3", null, {pool: "test-pool"})
await flushPromises();

expect(handler).toHaveBeenCalledTimes(1)
expect(handler).toHaveBeenCalledWith(src, "test-key3")
});
it('should watch over an instance in the pool', async () => {
let testPool = requestContext(null).getOrCreatePool("test-pool")

let handler = jest.fn()
testPool.watch("test-key4", handler)

let src = new AsyncState("test-key4", null, {pool: "test-pool"})
await flushPromises();
expect(handler).toHaveBeenCalledTimes(1)
expect(handler).toHaveBeenCalledWith(src, "test-key4")

handler.mockClear()
let src2 = new AsyncState("test-key5", null, {pool: "test-pool"})
await flushPromises();
expect(handler).not.toHaveBeenCalled()

handler.mockClear()
let src3 = new AsyncState("test-key4", null, {pool: "test-pool"})
testPool.set("test-key4", src3)
await flushPromises();
expect(handler).toHaveBeenCalledTimes(1)
expect(handler).toHaveBeenCalledWith(src3, "test-key4")
});
});
4 changes: 4 additions & 0 deletions packages/core/src/__tests__/async-state/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export const timeout = <T = any>(delay, ...value) => () => new Promise<T>(res =>
export const rejectionTimeout = (delay, ...value) => () => new Promise((res, rej) => setTimeout(() => {
rej(...value);
}, delay));

export function spyOnConsole(consoleMethod) {
return jest.spyOn(console, consoleMethod)
}
95 changes: 95 additions & 0 deletions packages/core/src/__tests__/async-state/wrapper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {run} from "../../wrapper";
import {ProducerProps} from "../../types";
import {beforeEach} from "@jest/globals";

describe('wrapper', () => {

let testProducerProps = { onAbort: () => {}} as ProducerProps<any, any, any, any>
let testIndicators = {aborted: false, cleared: false, done: false, index: 0}
let onSettled = jest.fn()

beforeEach(() => {
onSettled.mockClear()
})

it('should wrap sync producers and dont return an abort fn', () => {
let abort = run(
() => 5,
testProducerProps,
testIndicators,
onSettled,
undefined, // retry config
undefined, // callbacks
)

expect(abort).toBe(undefined)
expect(onSettled).toHaveBeenCalledWith(
5, "success", {"args": undefined, "payload": undefined}, undefined)
});
it('should do nothing when aborted is true immediately after execution', () => {
let abort = run(
() => 5,
testProducerProps,
{...testIndicators, aborted: true},
onSettled,
undefined, // retry config
undefined, // callbacks
)

expect(abort).toBe(undefined)
expect(onSettled).not.toHaveBeenCalled()
});
it('should do nothing when aborted is true immediately after throw in execution', () => {
let abort = run(
() => {throw 6},
testProducerProps,
{...testIndicators, aborted: true},
onSettled,
undefined, // retry config
undefined, // callbacks
)

expect(abort).toBe(undefined)
expect(onSettled).not.toHaveBeenCalled()
});
it('should walk sync generator', () => {
function* simpleGen() {
yield 1
yield 2
return yield 3
}
let abort = run(
simpleGen,
testProducerProps,
testIndicators,
onSettled,
undefined, // retry config
undefined, // callbacks
)

expect(abort).toBe(undefined)
expect(onSettled).toHaveBeenCalledWith(
3, "success", {"args": undefined, "payload": undefined}, undefined
)
});
it('should walk sync throwing generator', () => {
function* simpleGen() {
yield 1
yield 2
throw 6
}
let abort = run(
simpleGen,
testProducerProps,
testIndicators,
onSettled,
undefined, // retry config
undefined, // callbacks
)

expect(abort).toBe(undefined)
expect(onSettled).toHaveBeenCalledWith(
6, "error", {"args": undefined, "payload": undefined}, undefined
)
});
});
12 changes: 11 additions & 1 deletion packages/core/src/__tests__/state-hook/get-flags.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
AUTO_RUN,
AUTO_RUN, CHANGE_EVENTS,
CONFIG_FUNCTION,
CONFIG_OBJECT,
CONFIG_SOURCE,
Expand Down Expand Up @@ -261,6 +261,16 @@ describe('resolveFlags', () => {
.toEqual(CONFIG_OBJECT | AUTO_RUN);

});
it('should infer flags from overrides object', () => {
expect(resolveFlags({
key: "test",
payload: {},
producer: () => 5,
}, pool, {lazy: false, events: {
change: () => {}
}}))
.toEqual(CONFIG_OBJECT | AUTO_RUN | CHANGE_EVENTS);
});
});

});
Loading