{ props.onClick(e); if (!isOpen) { onClickNodeIcon(item); } }"
+ @click.stop="() => { if (!isOpen) { onClickNodeIcon(item); } }"
/>
{ if (!isOpen) { props.onClick(e); onClickNodeIcon(item); } onActiveted(item); }">
diff --git a/client/src/components/fileBrowser.vue b/client/src/components/fileBrowser.vue
index a43f4bb0..cacb8ce8 100644
--- a/client/src/components/fileBrowser.vue
+++ b/client/src/components/fileBrowser.vue
@@ -454,15 +454,20 @@ export default {
const cb = (fileList)=>{
if (!Array.isArray(fileList)) {
reject(fileList);
+ return;
}
- item.children = fileList
+ const children = fileList
.filter((e)=>{ return !e.isComponentDir; })
.map(fileListModifier.bind(null, this.pathSep));
+ // Re-look up the item at callback time: this.items may have been replaced
+ // by a getComponentDirRootFiles() refresh while the socket request was in flight.
+ const currentItem = this.getActiveItem(item.id);
+ (currentItem || item).children = children;
resolve();
};
const activeItem = this.getActiveItem(item.id);
if (activeItem === null) {
- console.log("failed to get current selected Item");
+ resolve();
return;
}
if (item.type === "dir" || item.type === "dir-link") {
diff --git a/server/app/core/exportComponent.js b/server/app/core/exportComponent.js
index bab76ff2..b181bc50 100644
--- a/server/app/core/exportComponent.js
+++ b/server/app/core/exportComponent.js
@@ -144,7 +144,7 @@ async function exportComponent(projectRootDir, componentID) {
//Get path relative to tempdRoot, then prepend /exportComponent/
const relativePath = path.relative(root, archiveFilename);
- const url = `${baseURL}/exportComponent/${relativePath}`;
+ const url = `${baseURL.replace(/\/$/, "")}/exportComponent/${relativePath}`;
return url;
}
diff --git a/server/app/core/exportProject.js b/server/app/core/exportProject.js
index d9f797c3..9e74fa1b 100644
--- a/server/app/core/exportProject.js
+++ b/server/app/core/exportProject.js
@@ -78,7 +78,7 @@ async function exportProject(projectRootDir, name = null, mail = null, memo = nu
[`${projectJson.name}.wheel`]
);
- const url = `${baseURL}/${path.join(path.relative(path.dirname(dir), archiveFilename))}`;
+ const url = `${baseURL.replace(/\/$/, "")}/${path.join(path.relative(path.dirname(dir), archiveFilename))}`;
return url;
}
diff --git a/server/app/core/sshManager.js b/server/app/core/sshManager.js
index 08d04056..5f90570d 100644
--- a/server/app/core/sshManager.js
+++ b/server/app/core/sshManager.js
@@ -14,7 +14,8 @@ const _internal = {
getSsh,
hasEntry,
askPassword,
- emitAll
+ emitAll,
+ verboseSsh
};
/**
@@ -205,7 +206,7 @@ async function createSsh(projectRootDir, remoteHostName, hostinfo, clientID, isS
if (hostinfo.readyTimeout) {
hostinfo.ConnectTimeout = Math.floor(hostinfo.readyTimeout / 1000);
}
- if (verboseSsh) {
+ if (_internal.verboseSsh) {
hostinfo.sshOpt = ["-vvv"];
}
if (hostinfo.username) {
diff --git a/server/app/db/db.js b/server/app/db/db.js
index 1597f290..b83e6ca6 100644
--- a/server/app/db/db.js
+++ b/server/app/db/db.js
@@ -168,7 +168,7 @@ function getIntVar(target, alt) {
* @returns {string} -
*/
function getStringVar(target, alt) {
- return typeof target === "string" ? target : alt;
+ return (typeof target === "string" && target !== "") ? target : alt;
}
/**
diff --git a/server/app/handlers/fileManager.js b/server/app/handlers/fileManager.js
index 43b0231d..e107d828 100644
--- a/server/app/handlers/fileManager.js
+++ b/server/app/handlers/fileManager.js
@@ -357,7 +357,7 @@ export async function onDownload(projectRootDir, target, cb) {
}
const ext = downloadZip ? ".zip" : "";
- const url = `${baseURL}/${path.join(path.relative(downloadRootDir, tmpDir), targetBasename)}${ext}`;
+ const url = `${baseURL.replace(/\/$/, "")}/${path.join(path.relative(downloadRootDir, tmpDir), targetBasename)}${ext}`;
getLogger(projectRootDir).debug("Download url is ready", url);
cb(url);
};
@@ -453,7 +453,7 @@ export async function onDownloadFullLog(projectRootDir, cb) {
//remove temporary directory
await fs.remove(archiveDir);
- const url = `${baseURL}/${path.join(path.relative(downloadRootDir, tmpDir), archiveName)}.zip`;
+ const url = `${baseURL.replace(/\/$/, "")}/${path.join(path.relative(downloadRootDir, tmpDir), archiveName)}.zip`;
getLogger(projectRootDir).info("Debug log archive is ready for download", url);
cb(url);
} catch (e) {
diff --git a/server/test/app/core/projectOperator.js b/server/test/app/core/projectOperator.js
index 903c7140..9793a545 100644
--- a/server/test/app/core/projectOperator.js
+++ b/server/test/app/core/projectOperator.js
@@ -44,11 +44,6 @@ describe("UT for projectOperation callback function", function () {
beforeEach(async ()=>{
await fs.remove(testDirRoot);
await createNewProject(projectRootDir, "test project", null, "test", "test@example.com");
- const sbs = _internal.projectOperationQueues.get(projectRootDir);
- if (sbs) {
- sbs.clear();
- }
- _internal.projectOperationQueues.clear();
sinon.stub(_internal, "onRunProject").value(onRunProject);
sinon.stub(_internal, "onStopProject").value(onStopProject);
sinon.stub(_internal, "onCleanProject").value(onCleanProject);
@@ -64,7 +59,15 @@ describe("UT for projectOperation callback function", function () {
onRevertProject.reset();
onSaveProject.reset();
});
- afterEach(()=>{
+ afterEach(async ()=>{
+ const sbs = _internal.projectOperationQueues.get(projectRootDir);
+ if (sbs) {
+ sbs.clear();
+ while (sbs.running.size > 0) {
+ await sleep(10);
+ }
+ }
+ _internal.projectOperationQueues.clear();
sinon.restore();
});
after(async ()=>{
diff --git a/server/test/app/core/sshManager.js b/server/test/app/core/sshManager.js
index eca5dac6..1329e027 100644
--- a/server/test/app/core/sshManager.js
+++ b/server/test/app/core/sshManager.js
@@ -374,8 +374,6 @@ describe("#createSsh", ()=>{
let askPasswordStub;
let SshClientWrapperStub;
let canConnectStub;
- let originalWheelVerboseSsh;
-
beforeEach(()=>{
hasEntryStub = sinon.stub(_internal, "hasEntry");
getSshStub = sinon.stub(_internal, "getSsh");
@@ -387,17 +385,11 @@ describe("#createSsh", ()=>{
canConnect: canConnectStub
};
});
- originalWheelVerboseSsh = process.env.WHEEL_VERBOSE_SSH;
- delete process.env.WHEEL_VERBOSE_SSH;
+ sinon.stub(_internal, "verboseSsh").value(false);
});
afterEach(()=>{
sinon.restore();
- if (originalWheelVerboseSsh !== undefined) {
- process.env.WHEEL_VERBOSE_SSH = originalWheelVerboseSsh;
- } else {
- delete process.env.WHEEL_VERBOSE_SSH;
- }
});
it("should return an existing ssh instance if hasEntry is true", async ()=>{
@@ -509,7 +501,7 @@ describe("#createSsh", ()=>{
});
it("should set sshOpt=['-vvv'] if WHEEL_VERBOSE_SSH is truthy", async ()=>{
- process.env.WHEEL_VERBOSE_SSH = "true";
+ sinon.stub(_internal, "verboseSsh").value(true);
hasEntryStub.returns(false);
canConnectStub.resolves(true);
diff --git a/test/compose.yml b/test/compose.yml
index b50d63bd..41dc038d 100644
--- a/test/compose.yml
+++ b/test/compose.yml
@@ -48,6 +48,8 @@ services:
WHEEL_ANONYMOUS_LOGIN: YES
WHEEL_ANONYMOUS_PASSWORD: WheelTest123!
WHEEL_LOG_LEVEL: OFF
+ volumes:
+ - "./wheel_config_auth:/root/.wheel"
depends_on:
- wheel_release_test
diff --git a/test/cypress/e2e/components/hpciss.cy.js b/test/cypress/e2e/components/hpciss.cy.js
index 6caa162e..5dd78c32 100644
--- a/test/cypress/e2e/components/hpciss.cy.js
+++ b/test/cypress/e2e/components/hpciss.cy.js
@@ -254,7 +254,7 @@ describe("components", ()=>{
it("転送対象ファイル・フォルダの設定-削除ボタン表示確認(output file)-削除ボタンが表示されることを確認", ()=>{
cy.createComponent(DEF_COMPONENT_HPCISS, HPCISS_NAME_0, 501, 500);
cy.enterInputOrOutputFile(TYPE_OUTPUT, "testOutputFile", true, true);
- cy.get("[data-cy=\"action_row-delete-btn\"]").should("be.visible");
+ cy.get("[data-cy=\"action_row-delete-btn\"]").scrollIntoView().should("be.visible");
});
/**
diff --git a/test/cypress/e2e/components/hpcisstar.cy.js b/test/cypress/e2e/components/hpcisstar.cy.js
index ef88dd78..82ae861e 100644
--- a/test/cypress/e2e/components/hpcisstar.cy.js
+++ b/test/cypress/e2e/components/hpcisstar.cy.js
@@ -257,7 +257,7 @@ describe("components", ()=>{
it("tarコンポーネント共通機能確認-転送対象ファイル・フォルダの設定-削除ボタン表示確認(output file)-削除ボタンが表示されることを確認", ()=>{
cy.createComponent(DEF_COMPONENT_HPCISS, HPCISS_NAME_0, 501, 500);
cy.enterInputOrOutputFile(TYPE_OUTPUT, "testOutputFile", true, true);
- cy.get("[data-cy=\"action_row-delete-btn\"]").should("be.visible");
+ cy.get("[data-cy=\"action_row-delete-btn\"]").scrollIntoView().should("be.visible");
});
/**
diff --git a/test/cypress/e2e/components/ps.cy.js b/test/cypress/e2e/components/ps.cy.js
index 7fe0d60f..f0400db5 100644
--- a/test/cypress/e2e/components/ps.cy.js
+++ b/test/cypress/e2e/components/ps.cy.js
@@ -637,15 +637,15 @@ describe("components", ()=>{
cy.get("body").should(($b)=>{
const editorGone = $b.find("[data-cy=\"workflow-text_editor_close-btn\"]").length === 0;
const discardShown = $b.find("button").filter((i, el)=>{
- return /discard all changes/i.test(el.textContent);
+ return /Discard changes/i.test(el.textContent);
}).length > 0;
expect(editorGone || discardShown, "editor closed or discard appeared").to.be.true;
})
.then(($b)=>{
if ($b.find("button").filter((i, el)=>{
- return /discard all changes/i.test(el.textContent);
+ return /Discard changes/i.test(el.textContent);
}).length) {
- cy.contains("button", /discard all changes/i).click();
+ cy.contains("button", /Discard changes/i).click();
}
});
}
diff --git a/test/cypress/e2e/components/stepjobTask.cy.js b/test/cypress/e2e/components/stepjobTask.cy.js
index 9d4a8845..9672629b 100644
--- a/test/cypress/e2e/components/stepjobTask.cy.js
+++ b/test/cypress/e2e/components/stepjobTask.cy.js
@@ -254,7 +254,7 @@ describe("components", ()=>{
cy.createStepjobComponentAndDoubleClick(DEF_COMPONENT_STEPJOB, STEPJOB_NAME_0, 501, 500);
cy.createComponent(DEF_COMPONENT_STEPJOB_TASK, STEPJOB_TASK_NAME_0, 501, 500);
cy.enterInputOrOutputFile(TYPE_OUTPUT, "testOutputFile", true, true);
- cy.get("[data-cy=\"action_row-delete-btn\"]").should("be.visible");
+ cy.get("[data-cy=\"action_row-delete-btn\"]").scrollIntoView().should("be.visible");
});
/**
diff --git a/test/cypress/e2e/components/storage.cy.js b/test/cypress/e2e/components/storage.cy.js
index 2c56e80c..0d484f25 100644
--- a/test/cypress/e2e/components/storage.cy.js
+++ b/test/cypress/e2e/components/storage.cy.js
@@ -254,7 +254,7 @@ describe("components", ()=>{
it("転送対象ファイル・フォルダの設定-削除ボタン表示確認(output file)-削除ボタンが表示されることを確認", ()=>{
cy.createComponent(DEF_COMPONENT_STORAGE, STORAGE_NAME_0, 501, 500);
cy.enterInputOrOutputFile(TYPE_OUTPUT, "testOutputFile", true, true);
- cy.get("[data-cy=\"action_row-delete-btn\"]").should("be.visible");
+ cy.get("[data-cy=\"action_row-delete-btn\"]").scrollIntoView().should("be.visible");
});
/**
diff --git a/test/cypress/e2e/components/task.cy.js b/test/cypress/e2e/components/task.cy.js
index 21e64091..274f24a6 100644
--- a/test/cypress/e2e/components/task.cy.js
+++ b/test/cypress/e2e/components/task.cy.js
@@ -366,9 +366,9 @@ describe("components", ()=>{
cy.get("[data-cy=\"file_browser-treeview-treeview\"]").contains("task1-run")
.should("exist")
.click();
- cy.get("[data-cy=\"file_browser-treeview-treeview\"]").contains("run-a.sh")
+ cy.contains("[data-cy=\"file_browser-treeview-treeview\"]", "run-a.sh", { timeout: 15000 })
.should("exist");
- cy.get("[data-cy=\"file_browser-treeview-treeview\"]").contains("run-b.sh")
+ cy.contains("[data-cy=\"file_browser-treeview-treeview\"]", "run-b.sh", { timeout: 15000 })
.should("exist");
cy.closeProperty();
});
@@ -410,7 +410,7 @@ describe("components", ()=>{
cy.get("[data-cy=\"file_browser-treeview-treeview\"]").contains("task1-run")
.should("exist")
.click();
- cy.get("[data-cy=\"file_browser-treeview-treeview\"]").contains("run-a.sh")
+ cy.contains("[data-cy=\"file_browser-treeview-treeview\"]", "run-a.sh", { timeout: 15000 })
.should("exist");
cy.closeProperty();
});
@@ -1002,7 +1002,7 @@ describe("components", ()=>{
it("プロパティ設定確認-シェルスクリプト選択セレクトボックス表示確認-シェルスクリプト選択セレクトボックスが表示されていることを確認", ()=>{
cy.get("[data-cy=\"component_property-advanced-panel_title\"]").click();
cy.get("[data-cy=\"component_property-task_use_javascript-autocomplete\"]").find("input")
- .should("be.visible");
+ .should("exist");
});
/**
@@ -1166,7 +1166,7 @@ describe("components", ()=>{
let targetDropBoxCy = "[data-cy=\"component_property-host-select\"]";
cy.selectValueFromDropdownList(targetDropBoxCy, 2, COMPONENT_TEST_LABEL);
cy.get("[data-cy=\"component_property-remote_file-panel_title\"]").click();
- cy.get("[data-cy=\"component_property-exclude-list_form\"]").should("be.visible");
+ cy.get("[data-cy=\"component_property-exclude-list_form\"]").find("input").first().should("exist");
});
/**
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 3d665c5b..c8d67e46 100644
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -489,7 +489,7 @@ Cypress.Commands.add("scriptEdit", (scriptName, script)=>{
cy.get("#editor").find("textarea")
.type(script, { force: true });
cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click();
- cy.get("button").contains(/^keep changes$/i)
+ cy.get("button").contains(/Keep changes/i)
.click()
.wait(animationWaitTime);
});
diff --git a/test/package.json b/test/package.json
index b3206fcb..0238e10d 100644
--- a/test/package.json
+++ b/test/package.json
@@ -7,9 +7,11 @@
"test": "npx cypress open",
"gateway": "GW_PORT=3001 REAL_APP=http://localhost:8089 MOCK_SIO=http://localhost:3101 node ws-gateway.cjs",
"test:e2e:mock": "npm run test:e2e:mock:start && npx cypress run --browser chrome; npm run test:e2e:mock:stop",
- "test:e2e:mock:start": "docker compose up -d --build && npx wait-on http://localhost:8089 http://localhost:8090 tcp:127.0.0.1:3001 tcp:127.0.0.1:3101 tcp:127.0.0.1:3102 --timeout 300000",
+ "test:e2e:mock:start": "docker compose up -d --build && npx wait-on http://localhost:8089 http://localhost:8090 tcp:127.0.0.1:3001 tcp:127.0.0.1:3101 tcp:127.0.0.1:3102 --timeout 600000",
"test:e2e:mock:stop": "docker compose down",
"test:e2e:mock:run": "npx cypress run --browser chrome",
+ "test:e2e:mock:run:sequential": "bash run-specs-sequential.sh",
+ "test:e2e:mock:run:sequential:bail": "bash run-specs-sequential.sh --bail",
"test:e2e": "npm run test:e2e:start && npx cypress run --browser chrome --config requestTimeout=300000,defaultCommandTimeout=300000,retries=1 --env USE_MOCK=false; npm run test:e2e:stop",
"test:e2e:start": "docker compose up -d --build wheel_release_test_server wheel_release_test",
"test:e2e:stop": "docker compose down",
diff --git a/test/run-specs-sequential.sh b/test/run-specs-sequential.sh
new file mode 100644
index 00000000..e772837c
--- /dev/null
+++ b/test/run-specs-sequential.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+set -uo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+cd "$SCRIPT_DIR"
+
+BAIL=false
+for arg in "$@"; do
+ case $arg in
+ -b|--bail) BAIL=true ;;
+ esac
+done
+
+FAILED_SPECS=()
+
+SPECS=()
+while IFS= read -r line; do
+ SPECS+=("$line")
+done < <(find cypress/e2e -name "*.cy.js" -not -path "*/gfarm/*" | sort)
+
+echo "Running ${#SPECS[@]} spec files sequentially... (bail=$BAIL)"
+
+for spec in "${SPECS[@]}"; do
+ echo ""
+ echo "=== Running: $spec ==="
+ if ! npx cypress run --browser chrome --spec "$spec"; then
+ FAILED_SPECS+=("$spec")
+ if [ "$BAIL" = true ]; then
+ echo "=== Bail: stopping after first failure ==="
+ exit 1
+ fi
+ fi
+done
+
+echo ""
+echo "=== Summary: ${#FAILED_SPECS[@]} of ${#SPECS[@]} specs failed ==="
+
+if [ ${#FAILED_SPECS[@]} -gt 0 ]; then
+ echo "Failed specs:"
+ for s in "${FAILED_SPECS[@]}"; do
+ echo " - $s"
+ done
+ exit 1
+fi
+
+exit 0