Merge branch 'release_23.2' into dev

This commit is contained in:
mvdbeek
2024-02-14 18:44:36 +01:00
14 changed files with 236 additions and 117 deletions
@@ -29,8 +29,14 @@ const TASKS_CONFIG = {
enable_celery_tasks: true,
};
const getPurgedContentSelection = () => new Map([["FAKE_ID", { purged: true }]]);
const getNonPurgedContentSelection = () => new Map([["FAKE_ID", { purged: false }]]);
const getMenuSelectorFor = (option) => `[data-description="${option} option"]`;
const getPurgedSelection = () => new Map([["FAKE_ID", { purged: true }]]);
const getNonPurgedSelection = () => new Map([["FAKE_ID", { purged: false }]]);
const getVisibleSelection = () => new Map([["FAKE_ID", { visible: true }]]);
const getHiddenSelection = () => new Map([["FAKE_ID", { visible: false }]]);
const getDeletedSelection = () => new Map([["FAKE_ID", { deleted: true }]]);
const getActiveSelection = () => new Map([["FAKE_ID", { deleted: false }]]);
async function mountSelectionOperationsWrapper(config) {
mockFetcher.path("/api/configuration").method("get").mock({ data: config });
@@ -77,117 +83,155 @@ describe("History Selection Operations", () => {
expect(wrapper.find('[data-description="selected count"]').text()).toContain("10");
});
it("should display 'hide' option only on visible items", async () => {
const option = '[data-description="hide option"]';
it("should display 'hide' option on visible items", async () => {
const option = getMenuSelectorFor("hide");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "visible:true" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'hide' option when visible and hidden items are mixed", async () => {
const option = getMenuSelectorFor("hide");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "visible:any" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should not display 'hide' option when only hidden items are selected", async () => {
const option = getMenuSelectorFor("hide");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "visible:any", contentSelection: getHiddenSelection() });
expect(wrapper.find(option).exists()).toBe(false);
await wrapper.setProps({ filterText: "visible:false" });
expect(wrapper.find(option).exists()).toBe(false);
});
it("should display 'unhide' option on hidden items", async () => {
const option = '[data-description="unhide option"]';
expect(wrapper.find(option).exists()).toBe(false);
const option = getMenuSelectorFor("unhide");
await wrapper.setProps({ filterText: "visible:false" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'unhide' option when hidden and visible items are mixed", async () => {
const option = '[data-description="unhide option"]';
expect(wrapper.find(option).exists()).toBe(false);
const option = getMenuSelectorFor("unhide");
await wrapper.setProps({ filterText: "visible:any" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'delete' option on non-deleted items", async () => {
const option = '[data-description="delete option"]';
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:true" });
it("should not display 'unhide' option when only visible items are selected", async () => {
const option = getMenuSelectorFor("unhide");
await wrapper.setProps({
filterText: "visible:any",
contentSelection: getVisibleSelection(),
});
expect(wrapper.find(option).exists()).toBe(false);
});
it("should display 'delete' option on non-deleted items", async () => {
const option = getMenuSelectorFor("delete");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:false" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'delete' option on non-deleted items", async () => {
const option = getMenuSelectorFor("delete");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:false" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'delete' option when non-deleted and deleted items are mixed", async () => {
const option = '[data-description="delete option"]';
const option = getMenuSelectorFor("delete");
await wrapper.setProps({ filterText: "deleted:any" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'permanently delete' option always", async () => {
const option = '[data-description="purge option"]';
it("should not display 'delete' option when only deleted items are selected", async () => {
const option = getMenuSelectorFor("delete");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:true" });
await wrapper.setProps({ filterText: "deleted:any", contentSelection: getDeletedSelection() });
expect(wrapper.find(option).exists()).toBe(false);
});
it("should display 'permanently delete' option always", async () => {
const option = getMenuSelectorFor("purge");
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:any visible:any" });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'undelete' option on deleted and non-purged items", async () => {
const option = '[data-description="undelete option"]';
const option = getMenuSelectorFor("undelete");
expect(wrapper.find(option).exists()).toBe(false);
await wrapper.setProps({
filterText: "deleted:true",
contentSelection: getNonPurgedContentSelection(),
contentSelection: getNonPurgedSelection(),
});
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'undelete' option when non-purged items (deleted or not) are mixed", async () => {
const option = '[data-description="undelete option"]';
const option = getMenuSelectorFor("undelete");
await wrapper.setProps({
filterText: "deleted:any",
contentSelection: getNonPurgedContentSelection(),
contentSelection: getNonPurgedSelection(),
});
expect(wrapper.find(option).exists()).toBe(true);
});
it("should not display 'undelete' when is manual selection mode and all selected items are purged", async () => {
const option = '[data-description="undelete option"]';
it("should not display 'undelete' when only non-deleted items are selected", async () => {
const option = getMenuSelectorFor("undelete");
await wrapper.setProps({
filterText: "deleted:true",
contentSelection: getPurgedContentSelection(),
filterText: "deleted:any",
contentSelection: getActiveSelection(),
});
expect(wrapper.find(option).exists()).toBe(false);
});
it("should not display 'undelete' when only purged items are selected", async () => {
const option = getMenuSelectorFor("undelete");
await wrapper.setProps({
contentSelection: getPurgedSelection(),
isQuerySelection: false,
});
expect(wrapper.find(option).exists()).toBe(false);
});
it("should display 'undelete' option when is query selection mode and filtering by deleted", async () => {
const option = '[data-description="undelete option"]';
const option = getMenuSelectorFor("undelete");
// In query selection mode we don't know if some items may not be purged, so we allow to undelete
await wrapper.setProps({
filterText: "deleted:true",
contentSelection: getPurgedContentSelection(),
contentSelection: getPurgedSelection(),
isQuerySelection: true,
});
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display 'undelete' option when is query selection mode and filtering by any deleted state", async () => {
const option = '[data-description="undelete option"]';
const option = getMenuSelectorFor("undelete");
// In query selection mode we don't know if some items may not be purged, so we allow to undelete
await wrapper.setProps({
filterText: "deleted:any",
contentSelection: getPurgedContentSelection(),
isQuerySelection: true,
});
await wrapper.setProps({ filterText: "deleted:any", isQuerySelection: true });
expect(wrapper.find(option).exists()).toBe(true);
});
it("should display collection building options only on visible and non-deleted items", async () => {
it("should display collection building options only on active (non-deleted) items", async () => {
const buildListOption = '[data-description="build list"]';
const buildPairOption = '[data-description="build pair"]';
const buildListOfPairsOption = '[data-description="build list of pairs"]';
await wrapper.setProps({ filterText: "visible:true deleted:false" });
expect(wrapper.find(buildListOption).exists()).toBe(true);
expect(wrapper.find(buildPairOption).exists()).toBe(true);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(true);
await wrapper.setProps({ filterText: "visible:false" });
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
await wrapper.setProps({ filterText: "deleted:true" });
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
await wrapper.setProps({ filterText: "visible:any" });
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
await wrapper.setProps({ filterText: "visible:any deleted:false" });
expect(wrapper.find(buildListOption).exists()).toBe(true);
expect(wrapper.find(buildPairOption).exists()).toBe(true);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:any" });
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
@@ -12,10 +12,10 @@
<b-dropdown-text>
<span v-localize data-description="selected count">With {{ numSelected }} selected...</span>
</b-dropdown-text>
<b-dropdown-item v-if="showHidden" v-b-modal:show-selected-content data-description="unhide option">
<b-dropdown-item v-if="canUnhideSelection" v-b-modal:show-selected-content data-description="unhide option">
<span v-localize>Unhide</span>
</b-dropdown-item>
<b-dropdown-item v-else v-b-modal:hide-selected-content data-description="hide option">
<b-dropdown-item v-if="canHideSelection" v-b-modal:hide-selected-content data-description="hide option">
<span v-localize>Hide</span>
</b-dropdown-item>
<b-dropdown-item
@@ -25,7 +25,7 @@
<span v-localize>Undelete</span>
</b-dropdown-item>
<b-dropdown-item
v-if="!showStrictDeleted"
v-if="canDeleteSelection"
v-b-modal:delete-selected-content
data-description="delete option">
<span v-localize>Delete</span>
@@ -193,20 +193,28 @@ export default {
},
computed: {
/** @returns {Boolean} */
showHidden() {
return !HistoryFilters.checkFilter(this.filterText, "visible", true);
canUnhideSelection() {
return this.areAllSelectedHidden || (this.isAnyVisibilityAllowed && !this.areAllSelectedVisible);
},
/** @returns {Boolean} */
canHideSelection() {
return this.areAllSelectedVisible || (this.isAnyVisibilityAllowed && !this.areAllSelectedHidden);
},
/** @returns {Boolean} */
showDeleted() {
return !HistoryFilters.checkFilter(this.filterText, "deleted", false);
},
/** @returns {Boolean} */
showStrictDeleted() {
return HistoryFilters.checkFilter(this.filterText, "deleted", true);
canDeleteSelection() {
return this.areAllSelectedActive || (this.isAnyDeletedStateAllowed && !this.areAllSelectedDeleted);
},
/** @returns {Boolean} */
canUndeleteSelection() {
return this.showDeleted && (this.isQuerySelection || !this.areAllSelectedPurged);
},
/** @returns {Boolean} */
showBuildOptions() {
return !this.isQuerySelection && !this.showHidden && !this.showDeleted;
return !this.isQuerySelection && this.areAllSelectedActive && !this.showDeleted;
},
/** @returns {Boolean} */
showBuildOptionForAll() {
@@ -227,9 +235,6 @@ export default {
noTagsSelected() {
return this.selectedTags.length === 0;
},
canUndeleteSelection() {
return this.showDeleted && (this.isQuerySelection || !this.areAllSelectedPurged);
},
areAllSelectedPurged() {
for (const item of this.contentSelection.values()) {
if (Object.prototype.hasOwnProperty.call(item, "purged") && !item["purged"]) {
@@ -238,6 +243,44 @@ export default {
}
return true;
},
areAllSelectedVisible() {
for (const item of this.contentSelection.values()) {
if (Object.prototype.hasOwnProperty.call(item, "visible") && !item["visible"]) {
return false;
}
}
return true;
},
areAllSelectedHidden() {
for (const item of this.contentSelection.values()) {
if (Object.prototype.hasOwnProperty.call(item, "visible") && item["visible"]) {
return false;
}
}
return true;
},
areAllSelectedActive() {
for (const item of this.contentSelection.values()) {
if (Object.prototype.hasOwnProperty.call(item, "deleted") && item["deleted"]) {
return false;
}
}
return true;
},
areAllSelectedDeleted() {
for (const item of this.contentSelection.values()) {
if (Object.prototype.hasOwnProperty.call(item, "deleted") && !item["deleted"]) {
return false;
}
}
return true;
},
isAnyVisibilityAllowed() {
return HistoryFilters.checkFilter(this.filterText, "visible", "any");
},
isAnyDeletedStateAllowed() {
return HistoryFilters.checkFilter(this.filterText, "deleted", "any");
},
},
watch: {
hasSelection(newVal) {
@@ -52,6 +52,7 @@ export default {
props: {
creators: {
type: Array,
default: () => [],
},
},
data() {
@@ -61,8 +62,11 @@ export default {
};
},
watch: {
creators() {
this.creatorsCurrent = this.creators;
creators: {
handler(newCreators) {
this.creatorsCurrent = newCreators;
},
immediate: true,
},
},
methods: {
@@ -97,7 +97,7 @@ export default {
default: "",
},
creator: {
type: Object,
type: Array,
default: null,
},
version: {
+33 -45
View File
@@ -165,7 +165,6 @@
</template>
<script>
import axios from "axios";
import { Toast } from "composables/toast";
import { storeToRefs } from "pinia";
import Vue, { computed, onUnmounted, ref, unref } from "vue";
@@ -179,9 +178,11 @@ import { hide_modal } from "@/layout/modal";
import { getAppRoot } from "@/onload/loadConfig";
import { useScopePointerStore } from "@/stores/scopePointerStore";
import { LastQueue } from "@/utils/lastQueue";
import { errorMessageAsString } from "@/utils/simple-error";
import { Services } from "../services";
import { defaultPosition } from "./composables/useDefaultStepPosition";
import { fromSimple, toSimple } from "./modules/model";
import { fromSimple } from "./modules/model";
import { getModule, getVersions, loadWorkflow, saveWorkflow } from "./modules/services";
import { getStateUpgradeMessages } from "./modules/utilities";
import reportDefault from "./reportDefault";
@@ -275,6 +276,7 @@ export default {
});
function resetStores() {
hasChanges.value = false;
connectionStore.$reset();
stepStore.$reset();
stateStore.$reset();
@@ -319,6 +321,7 @@ export default {
annotation: null,
name: "Unnamed Workflow",
tags: this.workflowTags,
services: null,
stateMessages: [],
insertedStateMessages: [],
refactorActions: [],
@@ -382,6 +385,7 @@ export default {
},
},
async created() {
this.services = new Services();
this.lastQueue = new LastQueue();
await this._loadCurrent(this.id, this.version);
hide_modal();
@@ -533,30 +537,27 @@ export default {
onDownload() {
window.location = `${getAppRoot()}api/workflows/${this.id}/download?format=json-download`;
},
async doSaveAs(create = false) {
const rename_name = create ? this.name : this.saveAsName ?? `SavedAs_${this.name}`;
const rename_annotation = create ? this.annotation || "" : this.saveAsAnnotation ?? "";
// This is an old web controller endpoint that wants form data posted...
const formData = new FormData();
formData.append("workflow_name", rename_name);
formData.append("workflow_annotation", rename_annotation);
formData.append("from_tool_form", true);
formData.append("workflow_data", JSON.stringify(toSimple(this.id, this)));
async doSaveAs() {
if (!this.saveAsName && !this.nameValidate()) {
return;
}
const rename_name = this.saveAsName ?? `SavedAs_${this.name}`;
const rename_annotation = this.saveAsAnnotation ?? "";
try {
const response = await axios.post(`${getAppRoot()}workflow/save_workflow_as`, formData);
const newId = response.data;
if (!create) {
this.name = rename_name;
this.annotation = rename_annotation;
}
const newSaveAsWf = { ...this, name: rename_name, annotation: rename_annotation };
const { id, name, number_of_steps } = await this.services.createWorkflow(newSaveAsWf);
const message = `Created new workflow '${name}' with ${number_of_steps} steps.`;
this.hasChanges = false;
await this.routeToWorkflow(newId);
await this.routeToWorkflow(id);
Toast.success(message);
} catch (e) {
this.onWorkflowError("Saving workflow failed, please contact an administrator.");
const errorHeading = `Saving workflow as '${rename_name}' failed`;
this.onWorkflowError(errorHeading, errorMessageAsString(e) || "Please contact an administrator.", {
Ok: () => {
this.hideModal();
},
});
}
},
onSaveAs() {
@@ -597,34 +598,21 @@ export default {
return;
}
try {
// if nothing other than payload vars changed, just use `create` endpoint
if (!this.hasChanges) {
const payload = {
workflow_name: this.name,
workflow_annotation: this.annotation || "",
workflow_tags: this.tags,
};
const { data } = await axios.put(`${getAppRoot()}workflow/create`, payload);
const { id, message } = data;
await this.routeToWorkflow(id);
Toast.success(message);
} else {
// otherwise, use `save_as` endpoint to include steps, etc.
await this.doSaveAs(true);
const stepCount = Object.keys(this.steps).length;
Toast.success(
`Created workflow ${this.name} with ${stepCount} ${stepCount === 1 ? "step" : "steps"}.`
);
}
const { id, name, number_of_steps } = await this.services.createWorkflow(this);
const message = `Created new workflow '${name}' with ${number_of_steps} steps.`;
this.hasChanges = false;
await this.routeToWorkflow(id);
Toast.success(message);
} catch (e) {
this.onWorkflowError("Creating workflow failed"),
e || "Please contact an administrator.",
this.onWorkflowError(
"Creating workflow failed",
errorMessageAsString(e) || "Please contact an administrator.",
{
Ok: () => {
this.hideModal();
},
};
}
);
}
},
nameValidate() {
@@ -3,6 +3,8 @@ import axios from "axios";
import { withPrefix } from "utils/redirect";
import { rethrowSimple } from "utils/simple-error";
import { toSimple } from "./Editor/modules/model";
/** Workflow data request helper **/
export class Services {
async copyWorkflow(workflow) {
@@ -27,6 +29,16 @@ export class Services {
}
}
async createWorkflow(workflow) {
const url = withPrefix("/api/workflows");
try {
const { data } = await axios.post(url, { workflow: toSimple(workflow.id, workflow) });
return data;
} catch (e) {
rethrowSimple(e);
}
}
async deleteWorkflow(id) {
const url = withPrefix(`/api/workflows/${id}`);
try {
+7 -2
View File
@@ -1,6 +1,10 @@
import abc
import os
import time
from dataclasses import (
dataclass,
field,
)
from enum import Enum
from typing import (
Any,
@@ -88,19 +92,20 @@ class FilesSourceProperties(TypedDict):
browsable: NotRequired[bool]
@dataclass
class FilesSourceOptions:
"""Options to control behavior of file source operations, such as realize_to, write_from and list."""
# Indicates access to the FS operation with intent to write.
# Even if a file source is "writeable" some directories (or elements) may be restricted or read-only
# so those should be skipped while browsing with writeable=True.
writeable: Optional[bool]
writeable: Optional[bool] = False
# Property overrides for values initially configured through the constructor. For example
# the HTTPFilesSource passes in additional http_headers through these properties, which
# are merged with constructor defined http_headers. The interpretation of these properties
# are filesystem specific.
extra_props: Optional[FilesSourceProperties]
extra_props: Optional[FilesSourceProperties] = field(default_factory=lambda: FilesSourceProperties())
class EntryData(TypedDict):
+2 -3
View File
@@ -1244,15 +1244,13 @@ class MinimalJobWrapper(HasResourceParameters):
tool_evaluator = self._get_tool_evaluator(job)
compute_environment = compute_environment or self.default_compute_environment(job)
if hasattr(self.app, "interactivetool_manager"):
self.interactivetools = tool_evaluator.populate_interactivetools()
self.app.interactivetool_manager.create_interactivetool(job, self.tool, self.interactivetools)
tool_evaluator.set_compute_environment(compute_environment, get_special=get_special)
(
self.command_line,
self.version_command_line,
self.extra_filenames,
self.environment_variables,
self.interactivetools,
) = tool_evaluator.build()
job.command_line = self.command_line
@@ -2616,6 +2614,7 @@ class TaskWrapper(JobWrapper):
self.version_command_line,
extra_filenames,
self.environment_variables,
*_,
) = tool_evaluator.build()
self.extra_filenames.extend(extra_filenames)
+31 -4
View File
@@ -121,6 +121,7 @@ class ToolEvaluator:
self.environment_variables: List[Dict[str, str]] = []
self.version_command_line: Optional[str] = None
self.command_line: Optional[str] = None
self.interactivetools: List[Dict[str, Any]] = []
def set_compute_environment(self, compute_environment: ComputeEnvironment, get_special: Optional[Callable] = None):
"""
@@ -514,7 +515,12 @@ class ToolEvaluator:
# the paths rewritten.
self.__walk_inputs(self.tool.inputs, param_dict, rewrite_unstructured_paths)
def populate_interactivetools(self):
def _create_interactivetools_entry_points(self):
if hasattr(self.app, "interactivetool_manager"):
self.interactivetools = self._populate_interactivetools_template()
self.app.interactivetool_manager.create_interactivetool(self.job, self.tool, self.interactivetools)
def _populate_interactivetools_template(self):
"""
Populate InteractiveTools templated values.
"""
@@ -571,12 +577,21 @@ class ToolEvaluator:
compute environment.
"""
config_file = self.tool.config_file
global_tool_logs(
self._create_interactivetools_entry_points, config_file, "Building Interactive Tool Entry Points"
)
global_tool_logs(self._build_config_files, config_file, "Building Config Files")
global_tool_logs(self._build_param_file, config_file, "Building Param File")
global_tool_logs(self._build_command_line, config_file, "Building Command Line")
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line")
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables")
return self.command_line, self.version_command_line, self.extra_filenames, self.environment_variables
return (
self.command_line,
self.version_command_line,
self.extra_filenames,
self.environment_variables,
self.interactivetools,
)
def _build_command_line(self):
"""
@@ -819,7 +834,13 @@ class PartialToolEvaluator(ToolEvaluator):
def build(self):
config_file = self.tool.config_file
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables")
return self.command_line, self.version_command_line, self.extra_filenames, self.environment_variables
return (
self.command_line,
self.version_command_line,
self.extra_filenames,
self.environment_variables,
self.interactivetools,
)
class RemoteToolEvaluator(ToolEvaluator):
@@ -837,4 +858,10 @@ class RemoteToolEvaluator(ToolEvaluator):
global_tool_logs(self._build_param_file, config_file, "Building Param File")
global_tool_logs(self._build_command_line, config_file, "Building Command Line")
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line")
return self.command_line, self.version_command_line, self.extra_filenames, self.environment_variables
return (
self.command_line,
self.version_command_line,
self.extra_filenames,
self.environment_variables,
self.interactivetools,
)
+1 -1
View File
@@ -114,7 +114,7 @@ def main(TMPDIR, WORKING_DIRECTORY, IMPORT_STORE_DIRECTORY):
)
tool_evaluator.set_compute_environment(compute_environment=SharedComputeEnvironment(job_io=job_io, job=job_io.job))
with open(os.path.join(WORKING_DIRECTORY, "tool_script.sh"), "a") as out:
command_line, version_command_line, extra_filenames, environment_variables = tool_evaluator.build()
command_line, version_command_line, extra_filenames, environment_variables, *_ = tool_evaluator.build()
out.write(f'{version_command_line or ""}{command_line}')
@@ -247,7 +247,7 @@ class DatasetCollectionsService(ServiceBase, UsesLibraryMixinItems):
raise exceptions.RequestParameterInvalidException(
"Parameter instance_type not being 'history' is not yet implemented."
)
hdca: HistoryDatasetCollectionAssociation = self.collection_manager.get_dataset_collection_instance(
hdca: "HistoryDatasetCollectionAssociation" = self.collection_manager.get_dataset_collection_instance(
trans, "history", hdca_id
)
@@ -3,7 +3,7 @@
<container type="docker">galaxy/test-http-example:0.1</container>
</requirements>
<entry_points>
<entry_point name="Simple" requires_domain="True">
<entry_point name="Simple IT for $__user_email__" requires_domain="True">
<port>7000</port>
<url>/</url>
</entry_point>
+1 -4
View File
@@ -113,14 +113,11 @@ class MockEvaluator:
self.local_working_directory = local_working_directory
self.param_dict = {}
def populate_interactivetools(self):
return []
def set_compute_environment(self, *args, **kwds):
pass
def build(self):
return TEST_COMMAND, "", [], []
return TEST_COMMAND, "", [], [], []
class MockJobQueue:
+1 -1
View File
@@ -139,7 +139,7 @@ class TestToolEvaluator(TestCase, UsesApp):
self.tool.config_files.append(("conf1", None, "$thresh"))
self.tool._command_line = "prog1 $conf1"
self._set_compute_environment()
command_line, _, extra_filenames, _ = self.evaluator.build()
command_line, _, extra_filenames, *_ = self.evaluator.build()
assert len(extra_filenames) == 1
config_filename = extra_filenames[0]
config_basename = os.path.basename(config_filename)