From c13e79d21bbb5a92a7b249cc5827d11538c242d1 Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Fri, 14 Feb 2025 10:11:32 +1100 Subject: [PATCH] FIX: Query downloads were being passed an incorrect query object. (#359) This is a follow-up to d726c4889e5c5d7726ed1035c37d0cd60a3bb3d6. The previous change missed changing the name of the query object when passing it to QueryResultsWrapper, which resulted in the download links not working properly after a query was run. This change fixes that bug, and includes an acceptance test to ensure it stays fixed during future work on this plugin. --- .../admin-plugins-explorer-index.js | 5 -- .../plugins-explorer-queries-details.hbs | 2 +- test/javascripts/acceptance/run-query-test.js | 83 +++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-plugins-explorer-index.js b/assets/javascripts/discourse/controllers/admin-plugins-explorer-index.js index 9b28f88..3a64d5d 100644 --- a/assets/javascripts/discourse/controllers/admin-plugins-explorer-index.js +++ b/assets/javascripts/discourse/controllers/admin-plugins-explorer-index.js @@ -150,11 +150,6 @@ export default class PluginsExplorerController extends Controller { this.showCreate = true; } - @action - resetParams() { - this.selectedItem.resetParams(); - } - @action updateSortProperty(property) { if (this.sortByProperty === property) { diff --git a/assets/javascripts/discourse/templates/admin/plugins-explorer-queries-details.hbs b/assets/javascripts/discourse/templates/admin/plugins-explorer-queries-details.hbs index c64d1e6..791281e 100644 --- a/assets/javascripts/discourse/templates/admin/plugins-explorer-queries-details.hbs +++ b/assets/javascripts/discourse/templates/admin/plugins-explorer-queries-details.hbs @@ -217,7 +217,7 @@ {{/if}} \ No newline at end of file diff --git a/test/javascripts/acceptance/run-query-test.js b/test/javascripts/acceptance/run-query-test.js index 3f8d178..d9095bf 100644 --- a/test/javascripts/acceptance/run-query-test.js +++ b/test/javascripts/acceptance/run-query-test.js @@ -1,5 +1,6 @@ import { click, visit } from "@ember/test-helpers"; import { test } from "qunit"; +import sinon from "sinon"; import { acceptance } from "discourse/tests/helpers/qunit-helpers"; import { i18n } from "discourse-i18n"; @@ -7,6 +8,14 @@ acceptance("Data Explorer Plugin | Run Query", function (needs) { needs.user(); needs.settings({ data_explorer_enabled: true }); + needs.hooks.beforeEach(() => { + sinon.stub(window, "open"); + }); + + needs.hooks.afterEach(() => { + window.open.restore(); + }); + needs.pretender((server, helper) => { server.get("/admin/plugins/explorer/groups.json", () => { return helper.response([ @@ -203,6 +212,12 @@ acceptance("Data Explorer Plugin | Run Query", function (needs) { rows: [[0, null, false]], }); }); + + server.get("/session/csrf.json", function () { + return helper.response({ + csrf: "mgk906YLagHo2gOgM1ddYjAN4hQolBdJCqlY6jYzAYs= ", + }); + }); }); test("runs query and renders data and a chart", async function (assert) { @@ -233,6 +248,74 @@ acceptance("Data Explorer Plugin | Run Query", function (needs) { assert.dom("canvas").exists("the chart was rendered"); }); + test("runs query and is able to download the results", async function (assert) { + await visit("/admin/plugins/explorer/queries/-6"); + + await click("form.query-run button"); + + const createElement = document.createElement.bind(document); + const appendChild = document.body.appendChild.bind(document.body); + const removeChild = document.body.removeChild.bind(document.body); + + const finishedForm = sinon.promise(); + + let formElement; + + const formStub = sinon + .stub(document, "createElement") + .callsFake((tagName) => { + if (tagName === "form") { + formElement = { + fakeForm: true, + setAttribute: sinon.stub(), + appendChild: sinon.stub(), + submit: sinon.stub().callsFake(finishedForm.resolve), + }; + + return formElement; + } + + return createElement(tagName); + }); + + const appendChildStub = sinon + .stub(document.body, "appendChild") + .callsFake((el) => { + if (!el.fakeForm) { + return appendChild(el); + } + }); + + const removeChildStub = sinon + .stub(document.body, "removeChild") + .callsFake((el) => { + if (!el.fakeForm) { + return removeChild(el); + } + }); + + await click("div.result-info button:nth-child(1)"); + + await finishedForm; + + formStub.restore(); + appendChildStub.restore(); + removeChildStub.restore(); + + assert.ok(window.open.called, "window.open was called for downloading"); + assert.ok(formStub.called, "form was created for downloading"); + assert.ok(formElement.submit.called, "form was submitted for downloading"); + + assert.ok( + formElement.setAttribute.calledWith("action"), + "form action attribute was set" + ); + assert.ok( + formElement.setAttribute.calledWith("method", "post"), + "form method attribute was set to POST" + ); + }); + test("runs query and renders 0, false, and NULL values correctly", async function (assert) { await visit("/admin/plugins/explorer/queries/2");