diff --git a/scripts/story-dialog.test.mjs b/scripts/story-dialog.test.mjs index 2e3d704..fcf9841 100644 --- a/scripts/story-dialog.test.mjs +++ b/scripts/story-dialog.test.mjs @@ -306,6 +306,14 @@ test("story over 1500 chars is trimmed in the form URL and the user is told", () assert.match(t.el("story-status").textContent, /^Your story was trimmed to 1500 characters/); }); +test("story cut inside an emoji drops the half pair so the form URL still opens", () => { + const t = boot(); + t.fill({ ...VALID, "story-text": "a".repeat(STORY_MAX - 1) + "😀 tail" }); + t.submit(); + assert.equal(t.opened.length, 1, "submit opens the draft issue instead of throwing URIError"); + assert.equal(t.issueUrl().searchParams.get("story"), "a".repeat(STORY_MAX - 1)); +}); + test("story at exactly 1500 chars is not trimmed", () => { const t = boot(); t.fill({ ...VALID, "story-text": "y".repeat(STORY_MAX) }); diff --git a/stories/index.html b/stories/index.html index ffd359d..4ebcc5d 100644 --- a/stories/index.html +++ b/stories/index.html @@ -2,7 +2,7 @@ - + Contributor Stories — Hive Commons @@ -378,7 +378,10 @@

Open a contributor story draft

function truncateStory(story) { if (story.length <= STORY_MAX) return { text: story, truncated: false }; - return { text: story.slice(0, STORY_MAX).trimEnd(), truncated: true }; + var cut = story.slice(0, STORY_MAX); + // A cut inside a surrogate pair leaves a lone surrogate that encodeURIComponent rejects. + if (/[\uD800-\uDBFF]$/.test(cut)) cut = cut.slice(0, -1); + return { text: cut.trimEnd(), truncated: true }; } function markdown(data) {