snapshot of https://github.com/ueberdosis/hocuspocus.git @ cf3cfa355b4d3bb61c6b6c80071f71783e32470e (2026-09-15)
Some checks failed
build / lint (22) (push) Failing after 17s
build / test (22) (push) Failing after 8s
build / lint (23) (push) Failing after 2s
build / build (22) (push) Has been skipped
build / build (23) (push) Has been skipped
build / test (23) (push) Failing after 4s
docsearch / docsearch (push) Failing after 1s
Some checks failed
build / lint (22) (push) Failing after 17s
build / test (22) (push) Failing after 8s
build / lint (23) (push) Failing after 2s
build / build (22) (push) Has been skipped
build / build (23) (push) Has been skipped
build / test (23) (push) Failing after 4s
docsearch / docsearch (push) Failing after 1s
This commit is contained in:
275
tests/server/onDestroy.ts
Normal file
275
tests/server/onDestroy.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import test from "ava";
|
||||
import { newHocuspocus, newHocuspocusProvider } from "../utils/index.ts";
|
||||
import { retryableAssertion } from "../utils/retryableAssertion.ts";
|
||||
|
||||
test("executes the onDestroy hook and has the instance", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
const hocuspocus = await newHocuspocus(t, {
|
||||
async onDestroy({ instance }) {
|
||||
t.is(instance, hocuspocus);
|
||||
|
||||
resolve("done");
|
||||
},
|
||||
});
|
||||
|
||||
await hocuspocus.server!.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy works if no document is open", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
const hocuspocus = await newHocuspocus(t);
|
||||
|
||||
await hocuspocus.server!.destroy();
|
||||
|
||||
t.pass();
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("executes the onDestroy hook from a custom extension", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
class CustomExtension {
|
||||
async onDestroy() {
|
||||
t.pass();
|
||||
|
||||
resolve("done");
|
||||
}
|
||||
}
|
||||
|
||||
const hocuspocus = await newHocuspocus(t, {
|
||||
extensions: [new CustomExtension()],
|
||||
});
|
||||
|
||||
await hocuspocus.server!.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy closes all connections", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
const hocuspocus = await newHocuspocus(t);
|
||||
|
||||
const provider1 = newHocuspocusProvider(t, hocuspocus);
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider1.synced, true));
|
||||
|
||||
t.is(hocuspocus.getConnectionsCount(), 1);
|
||||
t.is(hocuspocus.getDocumentsCount(), 1);
|
||||
|
||||
await hocuspocus.server!.destroy();
|
||||
|
||||
t.is(hocuspocus.getConnectionsCount(), 0);
|
||||
t.is(hocuspocus.getDocumentsCount(), 0);
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy does not call onStoreDocument if nothing debounced", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
const server = await newHocuspocus(t, {
|
||||
async onStoreDocument() {
|
||||
t.fail();
|
||||
},
|
||||
});
|
||||
|
||||
const provider = newHocuspocusProvider(t, server);
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider.synced, true));
|
||||
|
||||
await server.server!.destroy();
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy does not call onStoreDocument after debounced onStoreDocument executes", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
let called = 0;
|
||||
|
||||
const server = await newHocuspocus(t, {
|
||||
debounce: 200,
|
||||
unloadImmediately: true,
|
||||
async onStoreDocument() {
|
||||
called += 1;
|
||||
},
|
||||
});
|
||||
|
||||
const provider = newHocuspocusProvider(t, server, {
|
||||
onSynced() {
|
||||
// Dummy change to trigger onStoreDocument
|
||||
provider.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider.synced, true));
|
||||
|
||||
// Wait for the debounced onStoreDocument to execute
|
||||
await new Promise((r) => setTimeout(r, 400));
|
||||
|
||||
await server.server!.destroy();
|
||||
|
||||
t.is(called, 1);
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy calls onStoreDocument before returning if debounced", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
let called = false;
|
||||
|
||||
const hocuspocus = await newHocuspocus(t, {
|
||||
async onStoreDocument() {
|
||||
called = true;
|
||||
},
|
||||
});
|
||||
|
||||
const provider = newHocuspocusProvider(t, hocuspocus, {
|
||||
onSynced() {
|
||||
// Dummy change to trigger onStoreDocument
|
||||
provider.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
|
||||
const provider1 = newHocuspocusProvider(t, hocuspocus);
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider1.synced, true));
|
||||
|
||||
t.is(called, false);
|
||||
await hocuspocus.server!.destroy();
|
||||
t.is(called, true);
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy calls onStoreDocument before returning, even with unloadImmediately=false if debounced", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
let called = false;
|
||||
|
||||
const hocuspocus = await newHocuspocus(t, {
|
||||
async onStoreDocument() {
|
||||
called = true;
|
||||
},
|
||||
unloadImmediately: false,
|
||||
});
|
||||
|
||||
const provider = newHocuspocusProvider(t, hocuspocus, {
|
||||
onSynced() {
|
||||
// Dummy change to trigger onStoreDocument
|
||||
provider.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
|
||||
const provider1 = newHocuspocusProvider(t, hocuspocus);
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider1.synced, true));
|
||||
await retryableAssertion(t, (t2) => t2.is(provider.synced, true));
|
||||
|
||||
t.is(called, false);
|
||||
await hocuspocus.server!.destroy();
|
||||
t.is(called, true);
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy calls onStoreDocument before returning, even with unloadImmediately=false, with multiple docs if debounced", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
let called = 0;
|
||||
|
||||
const hocuspocus = await newHocuspocus(t, {
|
||||
async onStoreDocument() {
|
||||
called += 1;
|
||||
},
|
||||
unloadImmediately: false,
|
||||
});
|
||||
|
||||
const provider1 = newHocuspocusProvider(t, hocuspocus, {
|
||||
name: "test1",
|
||||
onSynced() {
|
||||
provider1.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
const provider2 = newHocuspocusProvider(t, hocuspocus, {
|
||||
name: "test2",
|
||||
onSynced() {
|
||||
provider2.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
const provider3 = newHocuspocusProvider(t, hocuspocus, {
|
||||
name: "test3",
|
||||
onSynced() {
|
||||
provider3.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider1.synced, true));
|
||||
await retryableAssertion(t, (t2) => t2.is(provider2.synced, true));
|
||||
await retryableAssertion(t, (t2) => t2.is(provider3.synced, true));
|
||||
|
||||
// Wait for all changes to reach the server and trigger debounced stores
|
||||
await retryableAssertion(t, (t2) => {
|
||||
t2.true(hocuspocus.debouncer.isDebounced("onStoreDocument-test1"));
|
||||
t2.true(hocuspocus.debouncer.isDebounced("onStoreDocument-test2"));
|
||||
t2.true(hocuspocus.debouncer.isDebounced("onStoreDocument-test3"));
|
||||
});
|
||||
|
||||
t.is(called, 0);
|
||||
await hocuspocus.server!.destroy();
|
||||
await retryableAssertion(t, (t2) => t2.is(called, 3));
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
|
||||
test("destroy calls onStoreDocument before returning, with multiple docs if debounced", async (t) => {
|
||||
await new Promise(async (resolve) => {
|
||||
let called = 0;
|
||||
|
||||
const hocuspocus = await newHocuspocus(t, {
|
||||
async onStoreDocument() {
|
||||
called += 1;
|
||||
},
|
||||
unloadImmediately: true,
|
||||
});
|
||||
|
||||
const provider1 = newHocuspocusProvider(t, hocuspocus, {
|
||||
name: "test1",
|
||||
onSynced() {
|
||||
provider1.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
const provider2 = newHocuspocusProvider(t, hocuspocus, {
|
||||
name: "test2",
|
||||
onSynced() {
|
||||
provider2.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
const provider3 = newHocuspocusProvider(t, hocuspocus, {
|
||||
name: "test3",
|
||||
onSynced() {
|
||||
provider3.document.getArray("foo").push(["foo"]);
|
||||
},
|
||||
});
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(provider1.synced, true));
|
||||
await retryableAssertion(t, (t2) => t2.is(provider2.synced, true));
|
||||
await retryableAssertion(t, (t2) => t2.is(provider3.synced, true));
|
||||
|
||||
// Wait for all changes to reach the server and trigger debounced stores
|
||||
await retryableAssertion(t, (t2) => {
|
||||
t2.true(hocuspocus.debouncer.isDebounced("onStoreDocument-test1"));
|
||||
t2.true(hocuspocus.debouncer.isDebounced("onStoreDocument-test2"));
|
||||
t2.true(hocuspocus.debouncer.isDebounced("onStoreDocument-test3"));
|
||||
});
|
||||
|
||||
t.is(called, 0);
|
||||
await hocuspocus.server!.destroy();
|
||||
|
||||
await retryableAssertion(t, (t2) => t2.is(called, 3));
|
||||
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user