discourse-shared-edits/test/javascripts/unit/lib/rich-mode-sync-test.js
David Taylor f9906eb3fb
DEV: Fix rich-mode-sync unit tests for new plugin build system (#151)
With the new plugin build system, ES module exports become non-configurable,
preventing Sinon from stubbing them directly. This caused two unit tests
to fail when trying to stub `getMarkdownFromView` from the yjs-document module.

The fix adds dependency injection support to RichModeSync via a
`_getMarkdownFromView` constructor option, allowing tests to provide a
mock function without needing to stub module exports.
2026-03-09 09:46:25 +00:00

89 lines
2.4 KiB
JavaScript

import { setupTest } from "ember-qunit";
import { module, test } from "qunit";
import sinon from "sinon";
import RichModeSync from "discourse/plugins/discourse-shared-edits/discourse/lib/shared-edits/rich-mode-sync";
module("Discourse Shared Edits | Unit | rich-mode-sync", function (hooks) {
setupTest(hooks);
hooks.afterEach(function () {
sinon.restore();
});
test("skips syncing when serializer unexpectedly returns blank content", function (assert) {
const anomalyStub = sinon.stub();
const sync = new RichModeSync(this.owner, {
onSyncAnomaly: anomalyStub,
_getMarkdownFromView: () => "",
});
const xmlFragment = {
length: 1,
forEach(callback) {
callback("still here");
},
};
const text = {
toString() {
return "Existing collaborative content";
},
};
const doc = {
transact: sinon.spy(),
};
const result = sync.syncYTextFromXmlFragment(xmlFragment, text, doc);
assert.false(result, "sync reports no changes applied");
assert.true(doc.transact.notCalled, "does not mutate the Yjs document");
assert.true(
anomalyStub.calledOnceWith(
sinon.match.has("reason", "empty_serialization")
),
"invokes anomaly callback with context"
);
});
test("allows intentional blanking when fragment contains no text", function (assert) {
const anomalyStub = sinon.stub();
const sync = new RichModeSync(this.owner, {
onSyncAnomaly: anomalyStub,
_getMarkdownFromView: () => "",
});
const xmlFragment = {
length: 1,
forEach(callback) {
callback("");
},
};
const yText = {
_value: "Old value",
toString() {
return this._value;
},
delete(start, len) {
this._value =
this._value.slice(0, start) + this._value.slice(start + len);
},
insert(start, str) {
this._value =
this._value.slice(0, start) + str + this._value.slice(start);
},
};
const doc = {
transact: sinon.spy((cb) => cb()),
};
const result = sync.syncYTextFromXmlFragment(xmlFragment, yText, doc);
assert.true(result, "sync applied");
assert.true(doc.transact.calledOnce, "document updated");
assert.true(anomalyStub.notCalled, "no anomaly reported");
assert.strictEqual(yText.toString(), "", "text cleared");
});
});