mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-06-19 07:43:46 +08:00
Previously, uploads in the AI bot docked composer were completely non-functional due to two issues: a Glimmer modifier ordering race condition where the file input's `didInsert` fired before the parent container's `didInsert` (so `UppyUpload` was never bound to the file input), and the submit service rejected upload-only submissions because it early-returned on empty `raw` before considering attached uploads. This change fixes the initialization order by calling `uppyUpload.setup()` in `setupContainer` when the file input is already available, restructures the submit validation to allow upload-only posts and skip the min-length check when uploads are present, and adds vertical padding to the uploads container for better visual spacing. <img width="791" height="330" alt="Screenshot 2026-05-04 at 10 14 43" src="https://github.com/user-attachments/assets/7a0b8e38-f29e-40f7-bac7-361ab549474c" />
213 lines
6.3 KiB
JavaScript
Vendored
213 lines
6.3 KiB
JavaScript
Vendored
import { getOwner } from "@ember/owner";
|
|
import { setupTest } from "ember-qunit";
|
|
import { module, test } from "qunit";
|
|
import pretender, { response } from "discourse/tests/helpers/create-pretender";
|
|
|
|
module("Unit | Service | ai-bot-docked-submit", function (hooks) {
|
|
setupTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
const siteSettings = getOwner(this).lookup("service:site-settings");
|
|
siteSettings.min_personal_message_post_length = 10;
|
|
});
|
|
|
|
test("returns null when topicId is missing", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
const result = await service.submitReply({
|
|
topicId: null,
|
|
raw: "Hello world, this is long enough",
|
|
});
|
|
assert.strictEqual(result, null);
|
|
});
|
|
|
|
test("returns null when raw is empty and no uploads", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
const result = await service.submitReply({
|
|
topicId: 42,
|
|
raw: "",
|
|
uploads: [],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
assert.strictEqual(result, null);
|
|
});
|
|
|
|
test("returns null and alerts when raw is shorter than min length and no uploads", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
let requestsCount = 0;
|
|
pretender.post("/posts.json", () => {
|
|
requestsCount += 1;
|
|
return response(200, {});
|
|
});
|
|
|
|
const result = await service.submitReply({
|
|
topicId: 42,
|
|
raw: "hi",
|
|
uploads: [],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
|
|
assert.strictEqual(result, null);
|
|
assert.strictEqual(requestsCount, 0, "no POST when too short");
|
|
});
|
|
|
|
test("submits with only uploads and no raw text", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
let rawSent;
|
|
pretender.post("/posts.json", (request) => {
|
|
const params = new URLSearchParams(request.requestBody);
|
|
rawSent = params.get("raw");
|
|
return response(200, { id: 1, topic_id: 42 });
|
|
});
|
|
|
|
const result = await service.submitReply({
|
|
topicId: 42,
|
|
raw: "",
|
|
uploads: [
|
|
{
|
|
short_url: "upload://abc123.pdf",
|
|
original_filename: "document.pdf",
|
|
extension: "pdf",
|
|
filesize: 1024,
|
|
},
|
|
],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
|
|
assert.notStrictEqual(
|
|
result,
|
|
null,
|
|
"submission succeeds with only uploads"
|
|
);
|
|
assert.true(
|
|
rawSent.includes("upload://abc123.pdf"),
|
|
"upload markdown is sent"
|
|
);
|
|
});
|
|
|
|
test("skips min length check when uploads are present", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
let rawSent;
|
|
pretender.post("/posts.json", (request) => {
|
|
const params = new URLSearchParams(request.requestBody);
|
|
rawSent = params.get("raw");
|
|
return response(200, { id: 1, topic_id: 42 });
|
|
});
|
|
|
|
const result = await service.submitReply({
|
|
topicId: 42,
|
|
raw: "hi",
|
|
uploads: [
|
|
{
|
|
short_url: "upload://abc123.png",
|
|
original_filename: "screenshot.png",
|
|
extension: "png",
|
|
width: 400,
|
|
height: 300,
|
|
},
|
|
],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
|
|
assert.notStrictEqual(
|
|
result,
|
|
null,
|
|
"submission succeeds with short text + uploads"
|
|
);
|
|
assert.true(rawSent.includes("hi"), "raw text preserved");
|
|
assert.true(
|
|
rawSent.includes("upload://abc123.png"),
|
|
"upload markdown appended"
|
|
);
|
|
});
|
|
|
|
test("returns null when uploads are still in progress", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
let requestsCount = 0;
|
|
pretender.post("/posts.json", () => {
|
|
requestsCount += 1;
|
|
return response(200, {});
|
|
});
|
|
|
|
const result = await service.submitReply({
|
|
topicId: 42,
|
|
raw: "Long enough message here",
|
|
uploads: [],
|
|
inProgressUploadsCount: 2,
|
|
});
|
|
|
|
assert.strictEqual(result, null);
|
|
assert.strictEqual(requestsCount, 0);
|
|
});
|
|
|
|
test("POSTs raw + topic_id + nested_post flag", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
let submittedBody;
|
|
pretender.post("/posts.json", (request) => {
|
|
submittedBody = request.requestBody;
|
|
return response(200, { id: 999, topic_id: 42 });
|
|
});
|
|
|
|
const result = await service.submitReply({
|
|
topicId: 42,
|
|
raw: "Long enough message body",
|
|
uploads: [],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
|
|
assert.strictEqual(result.id, 999);
|
|
assert.true(submittedBody.includes("topic_id=42"));
|
|
assert.true(submittedBody.includes("nested_post=true"));
|
|
});
|
|
|
|
test("appends upload markdown to raw content", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
const formDataFromBody = (body) => {
|
|
const params = new URLSearchParams(body);
|
|
return params.get("raw");
|
|
};
|
|
let rawSent;
|
|
pretender.post("/posts.json", (request) => {
|
|
rawSent = formDataFromBody(request.requestBody);
|
|
return response(200, { id: 1, topic_id: 42 });
|
|
});
|
|
|
|
await service.submitReply({
|
|
topicId: 42,
|
|
raw: "Here is a file",
|
|
uploads: [
|
|
{
|
|
short_url: "upload://abc123.png",
|
|
original_filename: "screenshot.png",
|
|
extension: "png",
|
|
width: 400,
|
|
height: 300,
|
|
},
|
|
],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
|
|
assert.true(rawSent.includes("Here is a file"), "raw text preserved");
|
|
assert.true(
|
|
rawSent.includes("upload://abc123.png"),
|
|
"upload markdown appended"
|
|
);
|
|
});
|
|
|
|
test("loading state flips around the request", async function (assert) {
|
|
const service = getOwner(this).lookup("service:ai-bot-docked-submit");
|
|
pretender.post("/posts.json", () => response(200, { id: 1, topic_id: 42 }));
|
|
|
|
assert.false(service.loading, "initially not loading");
|
|
|
|
const promise = service.submitReply({
|
|
topicId: 42,
|
|
raw: "Long enough message body",
|
|
uploads: [],
|
|
inProgressUploadsCount: 0,
|
|
});
|
|
|
|
assert.true(service.loading, "loading while in-flight");
|
|
await promise;
|
|
assert.false(service.loading, "cleared after response");
|
|
});
|
|
});
|