0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-14 13:58:53 +08:00
discourse/plugins/discourse-data-explorer/test/javascripts/acceptance/new-query-test.js
Natalie Tay 2598983e02
UX: Data explorer improvements (#40605)
Small updates to data explorer:

For `/new` (AI query creation)
- Auto-runs the generated query and shows the result first, mirroring
/edit
- New admin-only POST queries/preview endpoint runs unsaved SQL so
results can be previewed before creating a query
- Chart / table / SQL segmented control similar to /edit
  - SQL with a resizable grippie
- "Save query" is now the primary button
- added a "Run" button.
- Saving transitions to /edit and auto-runs (?run=true).
- More breathing room between SQL and the name/description fields.

For `/edit`
- Disables Run / Save-and-run / Undo / Help / Delete while a query is
running or AI is generating.

Charts & tables (shared QueryResult)
- Smarter default view: falls back to table when a chart would silently
drop columns (e.g. user / username / reason / sum) pure-numeric series
still chart.
- long tables now cap at chart height with a chevron expand button, so
action buttons stay reachable (this was done before)
- /g/x/reports also have memory over which option (chart/table) was
chosen before

Copy (i18n)
- "Re-generate" to "Update prompt".
2026-06-05 19:31:37 +08:00

281 lines
7.7 KiB
JavaScript
Vendored

import { click, currentURL, fillIn, settled, visit } from "@ember/test-helpers";
import { test } from "qunit";
import {
acceptance,
publishToMessageBus,
} from "discourse/tests/helpers/qunit-helpers";
acceptance("New Query", function (needs) {
needs.user();
needs.settings({ data_explorer_enabled: true });
needs.pretender((server, helper) => {
server.get("/admin/plugins/discourse-data-explorer.json", () => {
return helper.response({
id: "discourse-data-explorer",
name: "discourse-data-explorer",
enabled: true,
has_settings: true,
humanized_name: "Data Explorer",
is_discourse_owned: true,
admin_route: {
label: "explorer.title",
location: "discourse-data-explorer",
use_new_show_route: true,
},
});
});
server.get("/admin/plugins/discourse-data-explorer/groups.json", () => {
return helper.response([]);
});
server.get("/admin/plugins/discourse-data-explorer/schema.json", () => {
return helper.response({
topics: [
{
column_name: "id",
data_type: "serial",
primary: true,
},
],
});
});
server.get("/admin/plugins/discourse-data-explorer/queries", () => {
return helper.response({
queries: [],
});
});
server.post("/admin/plugins/discourse-data-explorer/queries", () => {
return helper.response({
query: {
id: -15,
sql: "SELECT 1",
name: "foo",
description: "a test query",
param_info: [],
created_at: "2021-02-05T16:42:45.572Z",
username: "system",
group_ids: [],
last_run_at: "2021-02-08T15:37:49.188Z",
hidden: false,
user_id: -1,
},
});
});
server.get("/admin/plugins/discourse-data-explorer/queries/-15", () => {
return helper.response({
query: {
id: -15,
sql: "SELECT 1",
name: "foo",
description: "a test query",
param_info: [],
created_at: "2021-02-05T16:42:45.572Z",
username: "system",
group_ids: [],
last_run_at: "2021-02-08T15:37:49.188Z",
hidden: false,
user_id: -1,
},
});
});
});
test("renders the manual create form and transitions on submit", async function (assert) {
await visit("/admin/plugins/discourse-data-explorer/queries");
await click(".d-page-subheader .btn-primary");
assert.strictEqual(
currentURL(),
"/admin/plugins/discourse-data-explorer/queries/new"
);
assert
.dom(".query-new__manual-form .right-panel .schema")
.exists("schema sidebar renders");
assert
.dom(".query-new__manual-form .editor-panel .ace-wrapper")
.exists("SQL editor renders alongside the schema sidebar");
await fillIn(".query-new__manual-form [data-name='name'] input", "foo");
await fillIn(
".query-new__manual-form [data-name='description'] textarea",
"a test query"
);
await click(".query-new__manual-form .btn-primary");
assert.strictEqual(
currentURL(),
"/admin/plugins/discourse-data-explorer/queries/-15"
);
});
});
acceptance("New Query - AI", function (needs) {
needs.user();
needs.settings({
data_explorer_enabled: true,
data_explorer_ai_queries_enabled: true,
});
const GENERATION_ID = "test-generation";
needs.pretender((server, helper) => {
server.get("/admin/plugins/discourse-data-explorer.json", () => {
return helper.response({
id: "discourse-data-explorer",
name: "discourse-data-explorer",
enabled: true,
has_settings: true,
humanized_name: "Data Explorer",
is_discourse_owned: true,
admin_route: {
label: "explorer.title",
location: "discourse-data-explorer",
use_new_show_route: true,
},
});
});
server.get("/admin/plugins/discourse-data-explorer/groups.json", () =>
helper.response([])
);
server.get("/admin/plugins/discourse-data-explorer/schema.json", () =>
helper.response({ topics: [] })
);
server.get("/admin/plugins/discourse-data-explorer/queries", () =>
helper.response({ queries: [] })
);
server.post(
"/admin/plugins/discourse-data-explorer/queries/generate.json",
() =>
helper.response({ generation_id: GENERATION_ID, status: "generating" })
);
server.post(
"/admin/plugins/discourse-data-explorer/queries/preview.json",
() =>
helper.response({
success: true,
errors: [],
colrender: [],
result_count: 1,
columns: ["my_value"],
rows: [[23]],
duration: 1.2,
default_limit: 1000,
})
);
server.post("/admin/plugins/discourse-data-explorer/queries", () =>
helper.response({
query: {
id: -15,
sql: "SELECT 23 AS my_value",
name: "Generated",
description: "",
param_info: [],
group_ids: [],
hidden: false,
user_id: -1,
},
})
);
server.get("/admin/plugins/discourse-data-explorer/queries/-15", () =>
helper.response({
query: {
id: -15,
sql: "SELECT 23 AS my_value",
name: "Generated",
description: "",
param_info: [],
group_ids: [],
hidden: false,
user_id: -1,
},
})
);
server.post("/admin/plugins/discourse-data-explorer/queries/-15/run", () =>
helper.response({
success: true,
errors: [],
colrender: [],
result_count: 1,
columns: ["my_value"],
rows: [[23]],
duration: 1.2,
default_limit: 1000,
})
);
});
async function generate(prompt) {
await visit("/admin/plugins/discourse-data-explorer/queries/new");
await fillIn(".query-new__ai-textarea", prompt);
await click(".query-new__generate-btn");
await publishToMessageBus(
`/discourse-data-explorer/queries/ai-generation/${GENERATION_ID}`,
{
generation_id: GENERATION_ID,
status: "complete",
sql: "SELECT 23 AS my_value",
name: "Generated",
description: "",
}
);
await settled();
}
test("save query is the primary action and the result is shown first", async function (assert) {
await generate("show me a value");
assert
.dom(".query-new__save-btn.btn-primary")
.exists("the save query button gets the primary treatment");
assert
.dom(".query-new__run-btn")
.exists("a run button is available before saving");
assert
.dom(".query-results-modes")
.exists("a chart/table/sql segmented control is shown");
assert
.dom(".query-new__result-bar .query-new__result-about")
.hasText(
/result/,
"the result count sits on the same line as the toggle"
);
assert
.dom(".query-new__preview .query-results-table-wrapper")
.exists("the result is run and shown first, not the SQL");
await click(".query-results-modes input[value='sql']");
assert
.dom(".query-new__sql-editor .ace-wrapper")
.exists("the SQL is available behind its own tab");
});
test("saving transitions to the edit page and runs the query", async function (assert) {
await generate("show me a value");
await click(".query-new__save-btn");
assert.true(
currentURL().startsWith(
"/admin/plugins/discourse-data-explorer/queries/-15"
),
"transitions to the saved query"
);
assert.true(
currentURL().includes("run=true"),
"carries the auto-run flag so the query runs immediately"
);
});
});