Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable collapsing markdown elements #15260

Merged
merged 10 commits into from
Jan 9, 2023
4 changes: 2 additions & 2 deletions client/src/components/Markdown/Elements/HistoryLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@

<script>
import axios from "axios";
import { getAppRoot } from "onload/loadConfig";
import Vue from "vue";
import BootstrapVue from "bootstrap-vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { errorMessageAsString } from "utils/simple-error";
import { safePath } from "utils/redirect";

Vue.use(BootstrapVue);

Expand Down Expand Up @@ -54,7 +54,7 @@ export default {
methods: {
onClick() {
axios
.post(`${getAppRoot()}api/histories`, { history_id: this.args.history_id })
.post(safePath("/api/histories"), { history_id: this.args.history_id })
.then(() => {
this.imported = true;
})
Expand Down
105 changes: 26 additions & 79 deletions client/src/components/Markdown/Markdown.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="markdown-wrapper">
<LoadingSpan v-if="loading" />
<loading-span v-if="loading" />
<div v-else>
<div>
<sts-download-button
Expand Down Expand Up @@ -28,7 +28,7 @@
</span>
</div>
<b-badge variant="info" class="w-100 rounded mb-3">
<div class="float-left m-1">Published with Galaxy {{ getVersion }} on {{ getTime }}</div>
<div class="float-left m-1">Published with Galaxy {{ version }} on {{ time }}</div>
<div class="float-right m-1">Identifier {{ markdownConfig.id }}</div>
</b-badge>
<div>
Expand All @@ -41,47 +41,18 @@
</div>
<div v-for="(obj, index) in markdownObjects" :key="index" class="markdown-components">
<p v-if="obj.name == 'default'" class="text-justify m-2" v-html="obj.content" />
<div v-else-if="obj.name == 'generate_galaxy_version'" class="galaxy-version">
<pre><code>{{ getVersion }}</code></pre>
</div>
<div v-else-if="obj.name == 'generate_time'" class="galaxy-time">
<pre><code>{{ getTime }}</code></pre>
</div>
<HistoryLink v-else-if="obj.name == 'history_link'" :args="obj.args" :histories="histories" />
<HistoryDatasetAsImage v-else-if="obj.name == 'history_dataset_as_image'" :args="obj.args" />
<HistoryDatasetLink v-else-if="obj.name == 'history_dataset_link'" :args="obj.args" />
<HistoryDatasetIndex v-else-if="obj.name == 'history_dataset_index'" :args="obj.args" />
<InvocationTime v-else-if="obj.name == 'invocation_time'" :args="obj.args" :invocations="invocations" />
<JobMetrics v-else-if="obj.name == 'job_metrics'" :args="obj.args" />
<JobParameters v-else-if="obj.name == 'job_parameters'" :args="obj.args" />
<WorkflowDisplay v-else-if="obj.name == 'workflow_display'" :args="obj.args" :workflows="workflows" />
<Visualization v-else-if="obj.name == 'visualization'" :args="obj.args" />
<HistoryDatasetCollectionDisplay
v-else-if="obj.name == 'history_dataset_collection_display'"
:args="obj.args"
:collections="historyDatasetCollections" />
<ToolStd
v-else-if="['tool_stdout', 'tool_stderr'].includes(obj.name)"
:args="obj.args"
:name="obj.name"
:jobs="jobs" />
<HistoryDatasetDisplay
v-else-if="['history_dataset_embedded', 'history_dataset_display'].includes(obj.name)"
:args="obj.args"
:datasets="historyDatasets"
:embedded="obj.name == 'history_dataset_embedded'" />
<HistoryDatasetDetails
v-else-if="
[
'history_dataset_name',
'history_dataset_info',
'history_dataset_peek',
'history_dataset_type',
].includes(obj.name)
"
<markdown-container
v-else
:name="obj.name"
:args="obj.args"
:datasets="historyDatasets" />
:datasets="datasets"
:collections="collections"
:histories="histories"
:invocations="invocations"
:jobs="jobs"
:time="time"
:version="version"
:workflows="workflows" />
</div>
</div>
</div>
Expand All @@ -97,21 +68,9 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faDownload, faEdit } from "@fortawesome/free-solid-svg-icons";

import LoadingSpan from "components/LoadingSpan";
import HistoryDatasetAsImage from "./Elements/HistoryDatasetAsImage";
import HistoryDatasetDisplay from "./Elements/HistoryDatasetDisplay";
import HistoryDatasetLink from "./Elements/HistoryDatasetLink";
import HistoryDatasetIndex from "./Elements/HistoryDatasetIndex";
import HistoryDatasetCollectionDisplay from "./Elements/HistoryDatasetCollection/CollectionDisplay";
import HistoryDatasetDetails from "./Elements/HistoryDatasetDetails";
import HistoryLink from "./Elements/HistoryLink";
import InvocationTime from "./Elements/InvocationTime";
import JobMetrics from "./Elements/JobMetrics";
import JobParameters from "./Elements/JobParameters";
import ToolStd from "./Elements/ToolStd";
import WorkflowDisplay from "./Elements/Workflow/WorkflowDisplay";
import Visualization from "./Elements/Visualization";
import StsDownloadButton from "components/StsDownloadButton";
import LoadingSpan from "components/LoadingSpan.vue";
import StsDownloadButton from "components/StsDownloadButton.vue";
import MarkdownContainer from "./MarkdownContainer.vue";

const FUNCTION_VALUE_REGEX = `\\s*(?:[\\w_\\-]+|\\"[^\\"]+\\"|\\'[^\\']+\\')\\s*`;
const FUNCTION_CALL = `\\s*[\\w\\|]+\\s*=` + FUNCTION_VALUE_REGEX;
Expand All @@ -132,21 +91,9 @@ library.add(faDownload, faEdit);
export default {
store: store,
components: {
HistoryDatasetDetails,
HistoryDatasetAsImage,
HistoryDatasetCollectionDisplay,
HistoryDatasetDisplay,
HistoryDatasetIndex,
HistoryDatasetLink,
HistoryLink,
JobMetrics,
JobParameters,
LoadingSpan,
ToolStd,
WorkflowDisplay,
Visualization,
InvocationTime,
MarkdownContainer,
FontAwesomeIcon,
LoadingSpan,
StsDownloadButton,
},
props: {
Expand Down Expand Up @@ -175,20 +122,20 @@ export default {
return {
markdownObjects: [],
markdownErrors: [],
historyDatasets: {},
datasets: {},
histories: {},
historyDatasetCollections: {},
collections: {},
workflows: {},
jobs: {},
invocations: {},
loading: true,
};
},
computed: {
getVersion() {
return this.markdownConfig.generate_version || "Unknown Galaxy Version";
effectiveExportLink() {
return this.enable_beta_markdown_export ? this.exportLink : null;
},
getTime() {
time() {
const generateTime = this.markdownConfig.generate_time;
if (generateTime) {
const date = new Date(generateTime);
Expand All @@ -202,8 +149,8 @@ export default {
}
return "unavailable";
},
effectiveExportLink() {
return this.enable_beta_markdown_export ? this.exportLink : null;
version() {
return this.markdownConfig.generate_version || "Unknown Galaxy Version";
},
},
watch: {
Expand All @@ -221,9 +168,9 @@ export default {
const markdown = config.content || config.markdown;
this.markdownErrors = config.errors || [];
this.markdownObjects = this.splitMarkdown(markdown);
this.historyDatasets = config.history_datasets || {};
this.datasets = config.history_datasets || {};
this.histories = config.histories || {};
this.historyDatasetCollections = config.history_dataset_collections || {};
this.collections = config.history_dataset_collections || {};
this.workflows = config.workflows || {};
this.jobs = config.jobs || {};
this.invocations = config.invocations || {};
Expand Down
106 changes: 106 additions & 0 deletions client/src/components/Markdown/MarkdownContainer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import axios from "axios";
import flushPromises from "flush-promises";
import { mount } from "@vue/test-utils";
import { getLocalVue } from "tests/jest/helpers";
import MockAdapter from "axios-mock-adapter";
import { safePath } from "utils/redirect";
import MountTarget from "./MarkdownContainer.vue";

// mock routes
jest.mock("utils/redirect");
safePath.mockImplementation((url) => url);

const localVue = getLocalVue();
const axiosMock = new MockAdapter(axios);

async function mountComponent(propsData, apiMap = {}) {
axiosMock.reset();
for (const [method, apiDetails] of Object.entries(apiMap)) {
for (const [path, response] of Object.entries(apiDetails)) {
axiosMock[method](path).reply(200, response);
}
}
return mount(MountTarget, {
localVue,
propsData,
stubs: {
FontAwesomeIcon: true,
},
});
}

async function testCollapse(wrapper) {
const nolink = wrapper.find("a");
expect(nolink.exists()).toBe(false);
const collapse = "Click here to expand/collapse";
await wrapper.setProps({ args: { collapse } });
const link = wrapper.find("a");
expect(link.text()).toBe(collapse);
const container = wrapper.find(".collapse");
expect(container.attributes("style")).toBe("display: none;");
await link.trigger("click");
expect(container.attributes("style")).toBe("");
}

describe("MarkdownContainer", () => {
it("Renders version", async () => {
const version = "test_version";
const wrapper = await mountComponent({
name: "generate_galaxy_version",
args: {},
version,
});
const versionEl = wrapper.find(".galaxy-version");
expect(versionEl.exists()).toBe(true);
expect(versionEl.find("code").text()).toBe(version);
testCollapse(wrapper);
});

it("Renders time stamp", async () => {
const time = "test_time";
const wrapper = await mountComponent({
name: "generate_time",
args: {},
time,
});
const version = wrapper.find(".galaxy-time");
expect(version.exists()).toBe(true);
expect(version.find("code").text()).toBe(time);
testCollapse(wrapper);
});

it("Renders history link", async () => {
const wrapper = await mountComponent(
{
name: "history_link",
args: { history_id: "test_history_id" },
histories: { test_history_id: { name: "history_name" } },
},
{
onPost: { "/api/histories": {} },
}
);
const link = wrapper.find("a");
expect(link.text()).toBe("Click to Import History: history_name.");
await link.trigger("click");
const postedData = JSON.parse(axiosMock.history.post[0].data);
expect(postedData.history_id).toBe("test_history_id");
await flushPromises();
const error = wrapper.find(".text-success");
const message = error.find("span");
expect(message.text()).toBe("Successfully Imported History: history_name!");
});

it("Renders history link (with failing import error message)", async () => {
const wrapper = await mountComponent({
name: "history_link",
args: { history_id: "test_history_id" },
histories: { test_history_id: { name: "history_name" } },
});
await wrapper.find("a").trigger("click");
await flushPromises();
const error = wrapper.find(".text-danger");
const message = error.find("span");
expect(message.text()).toBe("Failed to Import History: history_name!");
});
});
Loading