-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from effector/fix-client-detection
Fix client detection and add tests for that
- Loading branch information
Showing
5 changed files
with
202 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// @vitest-environment happy-dom | ||
|
||
import { describe, test, expect } from "vitest"; | ||
import { | ||
createStore, | ||
createEvent, | ||
createEffect, | ||
fork, | ||
serialize, | ||
allSettled, | ||
combine, | ||
sample, | ||
} from "effector"; | ||
|
||
import { getScope } from "./get-scope"; | ||
|
||
const up = createEvent(); | ||
const longUpFx = createEffect(async () => { | ||
await new Promise((r) => setTimeout(r, 10)); | ||
}); | ||
const $count = createStore(0).on([up, longUpFx.done], (s) => s + 1); | ||
const $derived = $count.map((s) => ({ ref: s })); | ||
const $combined = combine({ ref: $count }); | ||
const $nestedCombined = combine({ ref: $derived }); | ||
|
||
const $sampled = sample({ | ||
source: { ref: $combined }, | ||
fn: (ref) => ref.ref.ref, | ||
}); | ||
|
||
const getFixedDate = () => new Date(0); | ||
const updateDate = createEvent<Date>(); | ||
const $specialData = createStore(getFixedDate(), { | ||
serialize: { | ||
write: (_date) => ({ lol: "jsonified view" }), | ||
read: (_json) => getFixedDate(), | ||
}, | ||
}).on($count, () => getFixedDate()); | ||
|
||
describe("getClientScope", () => { | ||
test("should handle server values injection on the fly", async () => { | ||
const serverScope = fork(); | ||
|
||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
|
||
const serverValues = serialize(serverScope); | ||
|
||
const clientScopeOne = getScope(); | ||
|
||
expect(clientScopeOne.getState($count)).toEqual(0); | ||
expect(clientScopeOne.getState($derived)).toEqual({ ref: 0 }); | ||
expect(clientScopeOne.getState($combined)).toEqual({ ref: 0 }); | ||
expect(clientScopeOne.getState($nestedCombined)).toEqual({ | ||
ref: { ref: 0 }, | ||
}); | ||
expect(clientScopeOne.getState($sampled)).toEqual(0); | ||
expect(clientScopeOne.getState(longUpFx.pending)).toEqual(false); | ||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(0); | ||
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate()); | ||
|
||
const promise = allSettled(longUpFx, { scope: clientScopeOne }); | ||
|
||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(1); | ||
|
||
const clientScopeTwo = getScope(serverValues); | ||
|
||
expect(clientScopeTwo.getState($count)).toEqual(3); | ||
expect(clientScopeOne.getState($derived)).toEqual({ ref: 3 }); | ||
expect(clientScopeOne.getState($combined)).toEqual({ ref: 3 }); | ||
expect(clientScopeOne.getState($nestedCombined)).toEqual({ | ||
ref: { ref: 3 }, | ||
}); | ||
expect(clientScopeOne.getState($sampled)).toEqual(3); | ||
expect(clientScopeOne.getState(longUpFx.pending)).toEqual(true); | ||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(1); | ||
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate()); | ||
|
||
await promise; | ||
|
||
expect(clientScopeTwo.getState($count)).toEqual(4); | ||
expect(clientScopeOne.getState($derived)).toEqual({ ref: 4 }); | ||
expect(clientScopeOne.getState($combined)).toEqual({ ref: 4 }); | ||
expect(clientScopeOne.getState($nestedCombined)).toEqual({ | ||
ref: { ref: 4 }, | ||
}); | ||
expect(clientScopeOne.getState($sampled)).toEqual(4); | ||
expect(clientScopeOne.getState(longUpFx.pending)).toEqual(false); | ||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(0); | ||
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate()); | ||
}); | ||
test("shallow navigation to same page", async () => { | ||
const serverScope = fork(); | ||
|
||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
|
||
const values = serialize(serverScope); | ||
|
||
const clientScopeOne = getScope(values); | ||
|
||
expect(clientScopeOne.getState($count)).toEqual(3); | ||
|
||
await allSettled(up, { scope: clientScopeOne }); | ||
|
||
expect(clientScopeOne.getState($count)).toEqual(4); | ||
|
||
// This imitates shallow navigation to same page, e.g. with different query params | ||
// | ||
// Next.js will reuse the same pageProps instance in this case | ||
// which will basically override current page state with initial one | ||
// | ||
// So we need to basically just ignore it, because | ||
// we already have the latest state in the client scope | ||
const clientScopeTwo = getScope(values); | ||
|
||
expect(clientScopeTwo.getState($count)).toEqual(4); | ||
}); | ||
}); | ||
|
||
describe("getScope implementation details", () => { | ||
test("should return same scope on client every time", () => { | ||
/** | ||
* Implementation detail that may change in the future | ||
*/ | ||
const scopeOne = getScope(); | ||
const scopeTwo = getScope(); | ||
|
||
expect(scopeOne === scopeTwo).toBe(true); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,15 @@ | ||
import { describe, test, expect } from "vitest"; | ||
import { | ||
createStore, | ||
createEvent, | ||
createEffect, | ||
fork, | ||
serialize, | ||
allSettled, | ||
combine, | ||
sample, | ||
} from "effector"; | ||
|
||
import { internalGetClientScope } from "./get-scope"; | ||
import { getScope } from "./get-scope"; | ||
|
||
const up = createEvent(); | ||
const longUpFx = createEffect(async () => { | ||
await new Promise((r) => setTimeout(r, 10)); | ||
}); | ||
const $count = createStore(0).on([up, longUpFx.done], (s) => s + 1); | ||
const $derived = $count.map((s) => ({ ref: s })); | ||
const $combined = combine({ ref: $count }); | ||
const $nestedCombined = combine({ ref: $derived }); | ||
describe('getScope implementation details', () => { | ||
test('should return new scope on server every time', () => { | ||
/** | ||
* Implementation detail that may change in the future | ||
*/ | ||
const scopeOne = getScope(); | ||
const scopeTwo = getScope(); | ||
|
||
const $sampled = sample({ | ||
source: { ref: $combined }, | ||
fn: (ref) => ref.ref.ref, | ||
}); | ||
|
||
const getFixedDate = () => new Date(0); | ||
const updateDate = createEvent<Date>(); | ||
const $specialData = createStore(getFixedDate(), { | ||
serialize: { | ||
write: (_date) => ({ lol: "jsonified view" }), | ||
read: (_json) => getFixedDate(), | ||
}, | ||
}).on($count, () => getFixedDate()); | ||
|
||
describe("getClientScope", () => { | ||
test("should handle server values injection on the fly", async () => { | ||
const serverScope = fork(); | ||
|
||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
|
||
const serverValues = serialize(serverScope); | ||
|
||
const clientScopeOne = internalGetClientScope(); | ||
|
||
expect(clientScopeOne.getState($count)).toEqual(0); | ||
expect(clientScopeOne.getState($derived)).toEqual({ ref: 0 }); | ||
expect(clientScopeOne.getState($combined)).toEqual({ ref: 0 }); | ||
expect(clientScopeOne.getState($nestedCombined)).toEqual({ | ||
ref: { ref: 0 }, | ||
}); | ||
expect(clientScopeOne.getState($sampled)).toEqual(0); | ||
expect(clientScopeOne.getState(longUpFx.pending)).toEqual(false); | ||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(0); | ||
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate()); | ||
|
||
const promise = allSettled(longUpFx, { scope: clientScopeOne }); | ||
|
||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(1); | ||
|
||
const clientScopeTwo = internalGetClientScope(serverValues); | ||
|
||
expect(clientScopeTwo.getState($count)).toEqual(3); | ||
expect(clientScopeOne.getState($derived)).toEqual({ ref: 3 }); | ||
expect(clientScopeOne.getState($combined)).toEqual({ ref: 3 }); | ||
expect(clientScopeOne.getState($nestedCombined)).toEqual({ | ||
ref: { ref: 3 }, | ||
}); | ||
expect(clientScopeOne.getState($sampled)).toEqual(3); | ||
expect(clientScopeOne.getState(longUpFx.pending)).toEqual(true); | ||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(1); | ||
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate()); | ||
|
||
await promise; | ||
|
||
expect(clientScopeTwo.getState($count)).toEqual(4); | ||
expect(clientScopeOne.getState($derived)).toEqual({ ref: 4 }); | ||
expect(clientScopeOne.getState($combined)).toEqual({ ref: 4 }); | ||
expect(clientScopeOne.getState($nestedCombined)).toEqual({ | ||
ref: { ref: 4 }, | ||
}); | ||
expect(clientScopeOne.getState($sampled)).toEqual(4); | ||
expect(clientScopeOne.getState(longUpFx.pending)).toEqual(false); | ||
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(0); | ||
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate()); | ||
}); | ||
test("shallow navigation to same page", async () => { | ||
const serverScope = fork(); | ||
|
||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
await allSettled(up, { scope: serverScope }); | ||
|
||
const values = serialize(serverScope); | ||
|
||
const clientScopeOne = internalGetClientScope(values); | ||
|
||
expect(clientScopeOne.getState($count)).toEqual(3); | ||
|
||
await allSettled(up, { scope: clientScopeOne }); | ||
|
||
expect(clientScopeOne.getState($count)).toEqual(4); | ||
|
||
// This imitates shallow navigation to same page, e.g. with different query params | ||
// | ||
// Next.js will reuse the same pageProps instance in this case | ||
// which will basically override current page state with initial one | ||
// | ||
// So we need to basically just ignore it, because | ||
// we already have the latest state in the client scope | ||
const clientScopeTwo = internalGetClientScope(values); | ||
|
||
expect(clientScopeTwo.getState($count)).toEqual(4); | ||
}); | ||
}); | ||
expect(scopeOne !== scopeTwo).toBe(true); | ||
}) | ||
}) |
Oops, something went wrong.