From 70d222cd66f864a00cb30b7daab2bad530f805ea Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Tue, 10 Feb 2026 16:54:53 +0900 Subject: [PATCH 01/14] test(cypress): add e2e test jobexecute jobExecute.cy.js --- test/cypress/e2e/jobExecute.cy.js | 169 ++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 test/cypress/e2e/jobExecute.cy.js diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js new file mode 100644 index 00000000..7d9671ea --- /dev/null +++ b/test/cypress/e2e/jobExecute.cy.js @@ -0,0 +1,169 @@ +describe("jobExecute", ()=>{ + //仮実装中なのでコマンドもここに書いておく + Cypress.Commands.add( + "selectDropdownOptionExactScoped", + (targetDropBoxCy, dropBoxNo, selectVal, options = {})=>{ + const timeout = options.timeout ?? 10000; + const index = dropBoxNo ?? 0; + + //1) 対象外枠を index で 1 件に限定 + cy.get(targetDropBoxCy, { timeout }) + .should("have.length.at.least", index + 1) + .eq(index) + .as("root"); + + //2) combobox を 1 件に限定して開く + cy.get("@root") + .find(".v-field[role=\"combobox\"], [role=\"combobox\"]", { timeout }) + .first() //← 念のため 1 件化 + .as("combo") + .click({ force: true }); + + //3) 自分の listbox を :visible で 1 件に限定 + cy.get("@combo").invoke("attr", "aria-controls") + .then((menuId)=>{ + expect(menuId, "aria-controls").to.be.a("string").and.not.be.empty; + + const listboxSel = `#${menuId} [role="listbox"]:visible, #${menuId}[role="listbox"]:visible`; + cy.get(listboxSel, { timeout }).should("have.length", 1); + + //4) 完全一致で候補を 1 件に限定してクリック + cy.get(listboxSel) + .find(".v-list-item, .v-list-item-title, .v-list-item__content") + .filter((_, el)=>{ return el.textContent?.trim() === selectVal; }) + .should("have.length.at.least", 1) + .first() + .scrollIntoView() + .should("be.visible") + .click({ force: true }); + }); + + //5) 選択表示の反映(autocomplete と select の両対応) + cy.get("@root").within(()=>{ + cy.get(".v-autocomplete__selection-text, .v-select__selection-text", { timeout }) + .should("have.length.at.least", 1) + .first() + .should("have.text", selectVal); + }); + } + ); + + const DEF_COMPONENT_TASK = "task"; + const TASK_NAME_0 = "task0"; + + before(()=>{ + return cy.removeAllProjects(); + }); + + beforeEach(()=>{ + cy.viewport("macbook-16"); + return cy.createAndOpenProject(); + }); + + after(()=>{ + return cy.removeAllProjects(); + }); + + /** + テストで使用するremotehost情報の設定 + 事前に設定しておけばよいため自動化は不要の可能性あり + */ + it.skip("リモートホスト設定作成", ()=>{ + const LABEL = Math.random().toString(36) + .substring(2, 10); + const HOST_NAME = "wheel_release_test_server"; + const PORT_NUMBER = 22; + const TEST_USER = "testuser"; + + cy.get("[data-cy=\"tool_bar-navi-icon\"]").click(); + cy.get("[data-cy=\"navigation-manage_remote_host-btn\"]").click(); + cy.get("[data-cy=\"remotehost-new_remote_host_setting-btn\"]").click(); + cy.get("[data-cy=\"add_new_host-label-text_field\"]").type(LABEL); + cy.get("[data-cy=\"add_new_host-hostname-text_field\"]").type(HOST_NAME); + cy.get("[data-cy=\"add_new_host-port_number_label-text_field\"]").type(`{selectall}{backspace}${PORT_NUMBER}`); + cy.get("[data-cy=\"add_new_host-user_id-text_field\"]").type(TEST_USER); + //Click on dialog title to trigger blur from all fields + cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").click(); + //Wait for OK button to be enabled + cy.get("[data-cy=\"add_new_host-ok-btn\"]", { timeout: 1000 }).should("not.be.disabled") + .click(); + cy.contains("tr", LABEL).find("[data-cy=\"action_row-edit-btn\"]") + .click(); + cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").should("be.visible"); + //ダイアログ内のテキスト確認 + cy.get("[data-cy=\"add_new_host-label-text_field\"]").find("input") + .should("have.value", LABEL); + //ダイアログ内のOKボタン + cy.get("[data-cy=\"add_new_host-ok-btn\"]").click(); + }); + + /** + localhostでのタスク実行 + 現状実行完了までを確認 + ファイルの生成の確認方法を検討中 + */ + it("localhostでのタスク実行", ()=>{ + cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); + + //シェルファイル作成 + const filename = "test.sh"; + //シェルファイル作成 - Files展開 + cy.get("[data-cy=\"component_property-files-panel_title\"]", { timeout: 10000 }) + .scrollIntoView() + .click({ force: true }); + //シェルファイル作成 - New File クリック + cy.get("[data-cy=\"file_browser-new_file-btn\"]", { timeout: 10000 }) + .scrollIntoView() + .click({ force: true }); + //シェルファイル作成 - ファイル名入力 + cy.get("[data-cy=\"file_browser-input-text_field\"]").type(filename); + //シェルファイル作成 - OK押下 + cy.get("[data-cy=\"versatile_dialog_Create_new_File-ok-btn\"]", { timeout: 10000 }).click({ force: true }); + //シェルファイル作成 - シェルファイル選択 + cy.contains(".v-list-item__content", filename, { timeout: 10000 }) + .scrollIntoView() + .should("be.visible") + .click(); + //シェルファイル作成 - edit 押下 + cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click({ force: true }); + //シェルファイル作成 - シェルの実行内容入力 + const text = `echo "hello" > ~/test_output_pbs.txt && hostname >> ~/test_output_pbs.txt`; + cy.get("#editor", { timeout: 10000 }) + .should("have.class", "ace_editor") + .click() + .find("textarea.ace_text-input") + .should("exist") + .type("{selectall}{backspace}", { force: true }) + .type(text, { force: true }); + //シェルファイル作成 - 閉じるボタン押下 + cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); + //シェルファイル作成 - 変更内容を保存 + cy.contains("button", /^keep changes$/i, { timeout: 1000 }) + .scrollIntoView() + .should("be.visible") + .and("not.be.disabled") + .click(); + + //script でシェルファイル選択 + const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; + cy.selectDropdownOptionExactScoped(scriptEle, 0, filename); + + //ローカルホスト選択 + const hostEle = "[data-cy=\"component_property-host-select\"]"; + cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); + + //プロパティを閉じる + cy.get("[data-cy=\"component_property-close-btn\"]").scrollIntoView(); + + //タスク実行 + cy.get("[data-cy=\"workflow-play-btn\"]").click(); + + //完了まち + cy.get("[data-cy=\"workflow-project_state-btn\"] .v-btn__content", { timeout: 30000 }) + .should(($el)=>{ + const text = $el.text().trim() + .replace(/\s+/g, " "); + expect(text).to.eq("status: finished"); + }); + }); +}); From 205b8fb76d594fe33bf7b999108b71bd90c33f90 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Fri, 13 Feb 2026 16:17:38 +0900 Subject: [PATCH 02/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit taskを2つ作成に変更 taskどうしを連結 一部処理をコマンドに再編集 要素の特性方法を改修 --- test/cypress/e2e/jobExecute.cy.js | 247 ++++++++++++++++++++++-------- 1 file changed, 185 insertions(+), 62 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 7d9671ea..41a6fc6f 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -5,6 +5,17 @@ describe("jobExecute", ()=>{ (targetDropBoxCy, dropBoxNo, selectVal, options = {})=>{ const timeout = options.timeout ?? 10000; const index = dropBoxNo ?? 0; + const caseSensitive = options.caseSensitive ?? true; + const partial = options.partial ?? false; + const verifyMode = options.verifyMode ?? "auto"; + const nthMatch = options.nthMatch ?? 0; + + const norm = (s)=>{ return (s ?? "").replace(/\s+/g, " ").trim(); }; + const escapeRegExp = (s)=>{ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; + + const pattern = partial + ? new RegExp(escapeRegExp(norm(selectVal)), caseSensitive ? "" : "i") + : new RegExp(`^${escapeRegExp(norm(selectVal))}$`, caseSensitive ? "" : "i"); //1) 対象外枠を index で 1 件に限定 cy.get(targetDropBoxCy, { timeout }) @@ -12,44 +23,171 @@ describe("jobExecute", ()=>{ .eq(index) .as("root"); - //2) combobox を 1 件に限定して開く + //2) combobox を 1 件に限定して開く(aria-expanded 同期) cy.get("@root") - .find(".v-field[role=\"combobox\"], [role=\"combobox\"]", { timeout }) - .first() //← 念のため 1 件化 + .find("[role=\"combobox\"]", { timeout }) + .should("have.length.at.least", 1) + .first() .as("combo") - .click({ force: true }); + .click(); + + cy.get("@combo") + .should("have.attr", "aria-expanded") + .and("match", /true/i); - //3) 自分の listbox を :visible で 1 件に限定 - cy.get("@combo").invoke("attr", "aria-controls") + //3) 自分の listbox を aria-controls から特定(1件化) + cy.get("@combo") + .invoke("attr", "aria-controls") .then((menuId)=>{ expect(menuId, "aria-controls").to.be.a("string").and.not.be.empty; + const baseSel = `#${menuId}`; + const listboxSel = `${baseSel} [role="listbox"], ${baseSel}[role="listbox"]`; - const listboxSel = `#${menuId} [role="listbox"]:visible, #${menuId}[role="listbox"]:visible`; - cy.get(listboxSel, { timeout }).should("have.length", 1); + cy.get(listboxSel, { timeout }) + .should("have.length", 1) + .as("listbox"); - //4) 完全一致で候補を 1 件に限定してクリック - cy.get(listboxSel) - .find(".v-list-item, .v-list-item-title, .v-list-item__content") - .filter((_, el)=>{ return el.textContent?.trim() === selectVal; }) - .should("have.length.at.least", 1) - .first() + //4) 完全一致/部分一致で候補を抽出 → .v-list-item をクリック + cy.get("@listbox") + .find(".v-list-item, [role=\"option\"]", { timeout }) + .filter((_, el)=>{ + //テキストは .v-list-item-title 優先、なければ el.textContent + const title + = el.querySelector(".v-list-item-title")?.textContent + ?? el.textContent; + return pattern.test(norm(title)); + }) + .should("have.length.at.least", nthMatch + 1) + .eq(nthMatch) .scrollIntoView() .should("be.visible") - .click({ force: true }); + .click(); + + //5) メニューが閉じたことを同期(必要なら) + cy.get("@combo") + .should("have.attr", "aria-expanded") + .and("match", /false/i); }); - //5) 選択表示の反映(autocomplete と select の両対応) + //6) 選択表示の反映(autocomplete と select の両対応) cy.get("@root").within(()=>{ - cy.get(".v-autocomplete__selection-text, .v-select__selection-text", { timeout }) - .should("have.length.at.least", 1) - .first() - .should("have.text", selectVal); + const verifyAuto = ()=>{ + //a) input[role="combobox"] の value で一致 + cy.get("input[role=\"combobox\"]", { timeout }) + .should("have.length.at.least", 1) + .first() + .invoke("val") + .then((val)=>{ return expect(pattern.test(norm(String(val)))).to.be.true; }); + }; + + const verifySelectionText = ()=>{ + cy.get(".v-autocomplete__selection-text, .v-select__selection-text", { timeout }) + .should("have.length.at.least", 1) + .first() + .invoke("text") + .then((txt)=>{ return expect(pattern.test(norm(txt))).to.be.true; }); + }; + + if (verifyMode === "input") { + verifyAuto(); + } else if (verifyMode === "selectionText") { + verifySelectionText(); + } else { + //auto: どちらでも通るよう二段構え + cy.wrap(null).then(()=>{ + let verified = false; + return cy + .get("input[role=\"combobox\"]", { timeout }) + .then(($ins)=>{ + if ($ins.length) { + const val = ($ins[0]).value; + verified = pattern.test(norm(val)); + } + }) + .then(()=>{ + if (!verified) { + return cy + .get(".v-autocomplete__selection-text, .v-select__selection-text", { timeout }) + .then(($nodes)=>{ + if ($nodes.length) { + const txt = $nodes.eq(0).text(); + verified = pattern.test(norm(txt)); + } + }); + } + }) + .then(()=>{ + expect( + verified, + "selected value reflected either in input value or selection text" + ).to.be.true; + }); + }); + } }); } ); + //ヘルパー: 正規表現エスケープ + const escapeRegExp = (s)=>{ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; + + Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ + const timeout = options.timeout ?? 15000; + return cy + .contains("div.v-snackbar__content", messageRe, { timeout }) + .should("be.visible"); + }); + + Cypress.Commands.add( + "createShellScript", + (filename, text)=>{ + //シェルファイル作成 - Files展開 + cy.get("[data-cy=\"component_property-files-panel_title\"]") + .scrollIntoView() + .click(); + //シェルファイル作成 - New File クリック + cy.get("[data-cy=\"file_browser-new_file-btn\"]") + .scrollIntoView() + .click(); + //シェルファイル作成 - ファイル名入力 + cy.get("[data-cy=\"file_browser-input-text_field\"]").type(filename); + //シェルファイル作成 - OK押下 + cy.get("[data-cy=\"versatile_dialog_Create_new_File-ok-btn\"]").click(); + //シェルファイル作成 - シェルファイル選択 + cy.contains(".v-list-item__content", filename) + .scrollIntoView() + .should("be.visible") + .click(); + //シェルファイル作成 - edit 押下 + cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click(); + //シェルファイル作成 - シェルの実行内容入力 + cy.get("#editor") + .should("have.class", "ace_editor") + .click() + .find("textarea.ace_text-input") + .should("exist") + .type("{selectall}{backspace}", { force: true }) + .type(text, { force: true }); + //シェルファイル作成 - 閉じるボタン押下 + cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); + //シェルファイル作成 - 変更内容を保存 + cy.contains("button", /^keep changes$/i) + .scrollIntoView() + .should("be.visible") + .and("not.be.disabled") + .click(); + + //保存確認 + cy.waitForSnackbar(new RegExp(`${escapeRegExp(filename)}\\s+saved\\s*$`, "i")); + + //待機 + cy.wait(300); + } + ); + const DEF_COMPONENT_TASK = "task"; const TASK_NAME_0 = "task0"; + const TASK_NAME_1 = "task1"; before(()=>{ return cy.removeAllProjects(); @@ -65,8 +203,8 @@ describe("jobExecute", ()=>{ }); /** - テストで使用するremotehost情報の設定 - 事前に設定しておけばよいため自動化は不要の可能性あり + テストで使用するremotehost情報の設定 + 事前に設定しておけばよいため自動化は不要の可能性あり */ it.skip("リモートホスト設定作成", ()=>{ const LABEL = Math.random().toString(36) @@ -98,51 +236,18 @@ describe("jobExecute", ()=>{ }); /** - localhostでのタスク実行 - 現状実行完了までを確認 - ファイルの生成の確認方法を検討中 + localhostでのタスク実行 + 現状実行完了までを確認 + ファイルの生成の確認方法を検討中 */ it("localhostでのタスク実行", ()=>{ cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); //シェルファイル作成 const filename = "test.sh"; - //シェルファイル作成 - Files展開 - cy.get("[data-cy=\"component_property-files-panel_title\"]", { timeout: 10000 }) - .scrollIntoView() - .click({ force: true }); - //シェルファイル作成 - New File クリック - cy.get("[data-cy=\"file_browser-new_file-btn\"]", { timeout: 10000 }) - .scrollIntoView() - .click({ force: true }); - //シェルファイル作成 - ファイル名入力 - cy.get("[data-cy=\"file_browser-input-text_field\"]").type(filename); - //シェルファイル作成 - OK押下 - cy.get("[data-cy=\"versatile_dialog_Create_new_File-ok-btn\"]", { timeout: 10000 }).click({ force: true }); - //シェルファイル作成 - シェルファイル選択 - cy.contains(".v-list-item__content", filename, { timeout: 10000 }) - .scrollIntoView() - .should("be.visible") - .click(); - //シェルファイル作成 - edit 押下 - cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click({ force: true }); - //シェルファイル作成 - シェルの実行内容入力 const text = `echo "hello" > ~/test_output_pbs.txt && hostname >> ~/test_output_pbs.txt`; - cy.get("#editor", { timeout: 10000 }) - .should("have.class", "ace_editor") - .click() - .find("textarea.ace_text-input") - .should("exist") - .type("{selectall}{backspace}", { force: true }) - .type(text, { force: true }); - //シェルファイル作成 - 閉じるボタン押下 - cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); - //シェルファイル作成 - 変更内容を保存 - cy.contains("button", /^keep changes$/i, { timeout: 1000 }) - .scrollIntoView() - .should("be.visible") - .and("not.be.disabled") - .click(); + + cy.createShellScript(filename, text); //script でシェルファイル選択 const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; @@ -153,13 +258,31 @@ describe("jobExecute", ()=>{ cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); //プロパティを閉じる - cy.get("[data-cy=\"component_property-close-btn\"]").scrollIntoView(); + cy.closeProperty(); + + // 2つ目 + cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); + + cy.createShellScript(filename, text); + + //script でシェルファイル選択 + cy.selectDropdownOptionExactScoped(scriptEle, 0, filename); + + //ローカルホスト選択 + cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); + + ////プロパティを閉じる + cy.closeProperty(); + + cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); //コンポーネント同士を接続 + + cy.wait(300); //タスク実行 cy.get("[data-cy=\"workflow-play-btn\"]").click(); //完了まち - cy.get("[data-cy=\"workflow-project_state-btn\"] .v-btn__content", { timeout: 30000 }) + cy.get("[data-cy=\"workflow-project_state-btn\"]", { timeout: 30000 }) .should(($el)=>{ const text = $el.text().trim() .replace(/\s+/g, " "); From 87bb23a4ffe9e4c64b74b24cfb1b67acec664fbb Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Mon, 16 Feb 2026 15:56:19 +0900 Subject: [PATCH 03/14] =?UTF-8?q?test(cypress):=20fix=20e2e=20test=20jobex?= =?UTF-8?q?ecute=20jobExecute.cy.js=20foreach=E5=86=85=E3=81=A7task2?= =?UTF-8?q?=E3=81=A4=E3=82=92=E5=AE=9F=E8=A1=8C=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4=20=E7=94=9F=E6=88=90?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E6=A4=9C=E8=A8=BC?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=B7=E3=82=A7=E3=83=AB=E3=82=92task1?= =?UTF-8?q?=E5=81=B4=E3=81=A7=E5=AE=9F=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cypress/e2e/jobExecute.cy.js | 63 ++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 41a6fc6f..851bdcb7 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -1,4 +1,7 @@ describe("jobExecute", ()=>{ + const TYPE_INPUT = "input"; + const TYPE_OUTPUT = "output"; + //仮実装中なのでコマンドもここに書いておく Cypress.Commands.add( "selectDropdownOptionExactScoped", @@ -188,6 +191,8 @@ describe("jobExecute", ()=>{ const DEF_COMPONENT_TASK = "task"; const TASK_NAME_0 = "task0"; const TASK_NAME_1 = "task1"; + const DEF_COMPONENT_FOREACH = "foreach"; + const FOREACH_NAME_0 = "foreach0"; before(()=>{ return cy.removeAllProjects(); @@ -241,41 +246,71 @@ describe("jobExecute", ()=>{ ファイルの生成の確認方法を検討中 */ it("localhostでのタスク実行", ()=>{ - cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); + + const fileNameTask1 = "task1.sh"; + const fileNameTask2 = "task2.sh"; + const codeTask1 = `echo "test" > message.txt`; + const codeTask2 = `cat message.txt >/dev/null 2>&1`; + const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; + const hostEle = "[data-cy=\"component_property-host-select\"]"; - //シェルファイル作成 - const filename = "test.sh"; - const text = `echo "hello" > ~/test_output_pbs.txt && hostname >> ~/test_output_pbs.txt`; + // foreach作成 + cy.createComponent(DEF_COMPONENT_FOREACH, FOREACH_NAME_0, 501, 500); + + // loop設定 + cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() + .click(); + cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") + .type(1); + cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") + .eq(1) + .click(); //Add input file button + + //プロパティを閉じる + cy.closeProperty(); - cy.createShellScript(filename, text); + // foreach作成 + cy.doubleClickComponentName(FOREACH_NAME_0); + // task 1つ目 + cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); + + //シェルファイル作成 + cy.createShellScript(fileNameTask1, codeTask1); + //script でシェルファイル選択 - const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; - cy.selectDropdownOptionExactScoped(scriptEle, 0, filename); + cy.selectDropdownOptionExactScoped(scriptEle, 0, fileNameTask1); //ローカルホスト選択 - const hostEle = "[data-cy=\"component_property-host-select\"]"; cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); + // ためし + cy.enterInputOrOutputFile(TYPE_OUTPUT, "message.txt", true, true); + //プロパティを閉じる cy.closeProperty(); - // 2つ目 + // task 2つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); - cy.createShellScript(filename, text); + cy.createShellScript(fileNameTask2, codeTask2); //script でシェルファイル選択 - cy.selectDropdownOptionExactScoped(scriptEle, 0, filename); + cy.selectDropdownOptionExactScoped(scriptEle, 0, fileNameTask2); //ローカルホスト選択 cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); - ////プロパティを閉じる - cy.closeProperty(); + // ためし + cy.enterInputOrOutputFile(TYPE_INPUT, "message.txt", true, true); - cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); //コンポーネント同士を接続 + //プロパティを閉じる + cy.closeProperty(); + + //コンポーネント同士を接続 + cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); + //待機 cy.wait(300); //タスク実行 From f4ff9eaa2e0207aae1e286c8e1f88c677829a4c3 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Mon, 16 Feb 2026 17:29:37 +0900 Subject: [PATCH 04/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 既存コマンドで対応可能な操作を差し替え taskスクリプト作成部分をコマンド化 --- test/cypress/e2e/jobExecute.cy.js | 220 ++++++------------------------ 1 file changed, 45 insertions(+), 175 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 851bdcb7..c7871dfa 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -1,138 +1,29 @@ describe("jobExecute", ()=>{ - const TYPE_INPUT = "input"; - const TYPE_OUTPUT = "output"; + /** + * エスケープ + * @param {string} s - 対象文字列 + * @returns {string} 修正文字列 + */ + function escapeRegExp(s) { + return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } //仮実装中なのでコマンドもここに書いておく - Cypress.Commands.add( - "selectDropdownOptionExactScoped", - (targetDropBoxCy, dropBoxNo, selectVal, options = {})=>{ - const timeout = options.timeout ?? 10000; - const index = dropBoxNo ?? 0; - const caseSensitive = options.caseSensitive ?? true; - const partial = options.partial ?? false; - const verifyMode = options.verifyMode ?? "auto"; - const nthMatch = options.nthMatch ?? 0; - - const norm = (s)=>{ return (s ?? "").replace(/\s+/g, " ").trim(); }; - const escapeRegExp = (s)=>{ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; - - const pattern = partial - ? new RegExp(escapeRegExp(norm(selectVal)), caseSensitive ? "" : "i") - : new RegExp(`^${escapeRegExp(norm(selectVal))}$`, caseSensitive ? "" : "i"); - - //1) 対象外枠を index で 1 件に限定 - cy.get(targetDropBoxCy, { timeout }) - .should("have.length.at.least", index + 1) - .eq(index) - .as("root"); - - //2) combobox を 1 件に限定して開く(aria-expanded 同期) - cy.get("@root") - .find("[role=\"combobox\"]", { timeout }) - .should("have.length.at.least", 1) - .first() - .as("combo") - .click(); - - cy.get("@combo") - .should("have.attr", "aria-expanded") - .and("match", /true/i); - - //3) 自分の listbox を aria-controls から特定(1件化) - cy.get("@combo") - .invoke("attr", "aria-controls") - .then((menuId)=>{ - expect(menuId, "aria-controls").to.be.a("string").and.not.be.empty; - const baseSel = `#${menuId}`; - const listboxSel = `${baseSel} [role="listbox"], ${baseSel}[role="listbox"]`; - - cy.get(listboxSel, { timeout }) - .should("have.length", 1) - .as("listbox"); - - //4) 完全一致/部分一致で候補を抽出 → .v-list-item をクリック - cy.get("@listbox") - .find(".v-list-item, [role=\"option\"]", { timeout }) - .filter((_, el)=>{ - //テキストは .v-list-item-title 優先、なければ el.textContent - const title - = el.querySelector(".v-list-item-title")?.textContent - ?? el.textContent; - return pattern.test(norm(title)); - }) - .should("have.length.at.least", nthMatch + 1) - .eq(nthMatch) - .scrollIntoView() - .should("be.visible") - .click(); - - //5) メニューが閉じたことを同期(必要なら) - cy.get("@combo") - .should("have.attr", "aria-expanded") - .and("match", /false/i); - }); + Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target)=>{ + const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; + const hostEle = "[data-cy=\"component_property-host-select\"]"; - //6) 選択表示の反映(autocomplete と select の両対応) - cy.get("@root").within(()=>{ - const verifyAuto = ()=>{ - //a) input[role="combobox"] の value で一致 - cy.get("input[role=\"combobox\"]", { timeout }) - .should("have.length.at.least", 1) - .first() - .invoke("val") - .then((val)=>{ return expect(pattern.test(norm(String(val)))).to.be.true; }); - }; + cy.createShellScript(scriptName, shellText); - const verifySelectionText = ()=>{ - cy.get(".v-autocomplete__selection-text, .v-select__selection-text", { timeout }) - .should("have.length.at.least", 1) - .first() - .invoke("text") - .then((txt)=>{ return expect(pattern.test(norm(txt))).to.be.true; }); - }; + //script でシェルファイル選択 + cy.selectValueFromDropdownList(scriptEle, 3, scriptName); - if (verifyMode === "input") { - verifyAuto(); - } else if (verifyMode === "selectionText") { - verifySelectionText(); - } else { - //auto: どちらでも通るよう二段構え - cy.wrap(null).then(()=>{ - let verified = false; - return cy - .get("input[role=\"combobox\"]", { timeout }) - .then(($ins)=>{ - if ($ins.length) { - const val = ($ins[0]).value; - verified = pattern.test(norm(val)); - } - }) - .then(()=>{ - if (!verified) { - return cy - .get(".v-autocomplete__selection-text, .v-select__selection-text", { timeout }) - .then(($nodes)=>{ - if ($nodes.length) { - const txt = $nodes.eq(0).text(); - verified = pattern.test(norm(txt)); - } - }); - } - }) - .then(()=>{ - expect( - verified, - "selected value reflected either in input value or selection text" - ).to.be.true; - }); - }); - } - }); - } - ); + //ローカルホスト選択 + cy.selectValueFromDropdownList(hostEle, 3, target); - //ヘルパー: 正規表現エスケープ - const escapeRegExp = (s)=>{ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; + //インプットファイル指定 + cy.enterInputOrOutputFile(inputType, ioFileName, true, true); + }); Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ const timeout = options.timeout ?? 15000; @@ -144,26 +35,26 @@ describe("jobExecute", ()=>{ Cypress.Commands.add( "createShellScript", (filename, text)=>{ - //シェルファイル作成 - Files展開 + //シェル作成 - Files展開 cy.get("[data-cy=\"component_property-files-panel_title\"]") .scrollIntoView() .click(); - //シェルファイル作成 - New File クリック + //シェル作成 - New File クリック cy.get("[data-cy=\"file_browser-new_file-btn\"]") .scrollIntoView() .click(); - //シェルファイル作成 - ファイル名入力 + //シェル作成 - ファイル名入力 cy.get("[data-cy=\"file_browser-input-text_field\"]").type(filename); - //シェルファイル作成 - OK押下 + //シェル作成 - OK押下 cy.get("[data-cy=\"versatile_dialog_Create_new_File-ok-btn\"]").click(); - //シェルファイル作成 - シェルファイル選択 + //シェル作成 - 選択 cy.contains(".v-list-item__content", filename) .scrollIntoView() .should("be.visible") .click(); - //シェルファイル作成 - edit 押下 + //シェル作成 - edit cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click(); - //シェルファイル作成 - シェルの実行内容入力 + //シェル作成 - 実行内容入力 cy.get("#editor") .should("have.class", "ace_editor") .click() @@ -171,9 +62,9 @@ describe("jobExecute", ()=>{ .should("exist") .type("{selectall}{backspace}", { force: true }) .type(text, { force: true }); - //シェルファイル作成 - 閉じるボタン押下 + //シェル作成 - 閉じるボタン cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); - //シェルファイル作成 - 変更内容を保存 + //シェル作成 - 変更内容を保存 cy.contains("button", /^keep changes$/i) .scrollIntoView() .should("be.visible") @@ -182,12 +73,11 @@ describe("jobExecute", ()=>{ //保存確認 cy.waitForSnackbar(new RegExp(`${escapeRegExp(filename)}\\s+saved\\s*$`, "i")); - - //待機 - cy.wait(300); } ); + const TYPE_INPUT = "input"; + const TYPE_OUTPUT = "output"; const DEF_COMPONENT_TASK = "task"; const TASK_NAME_0 = "task0"; const TASK_NAME_1 = "task1"; @@ -241,24 +131,21 @@ describe("jobExecute", ()=>{ }); /** - localhostでのタスク実行 - 現状実行完了までを確認 - ファイルの生成の確認方法を検討中 + * localhostでのタスク実行 + * 現状実行完了までを確認 + * ファイルの生成の確認方法を検討中 */ it("localhostでのタスク実行", ()=>{ - const fileNameTask1 = "task1.sh"; const fileNameTask2 = "task2.sh"; const codeTask1 = `echo "test" > message.txt`; const codeTask2 = `cat message.txt >/dev/null 2>&1`; - const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; - const hostEle = "[data-cy=\"component_property-host-select\"]"; - // foreach作成 + //foreach作成 cy.createComponent(DEF_COMPONENT_FOREACH, FOREACH_NAME_0, 501, 500); - // loop設定 - cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() + //loop設定 + cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() .click(); cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") .type(1); @@ -269,46 +156,29 @@ describe("jobExecute", ()=>{ //プロパティを閉じる cy.closeProperty(); - // foreach作成 + //foreach作成 cy.doubleClickComponentName(FOREACH_NAME_0); - // task 1つ目 + //task 1つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); - //シェルファイル作成 - cy.createShellScript(fileNameTask1, codeTask1); - - //script でシェルファイル選択 - cy.selectDropdownOptionExactScoped(scriptEle, 0, fileNameTask1); - - //ローカルホスト選択 - cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); - - // ためし - cy.enterInputOrOutputFile(TYPE_OUTPUT, "message.txt", true, true); + //シェル作成 + cy.setupTaskWithScriptAndIO(fileNameTask1, codeTask1, TYPE_OUTPUT, "message.txt", "localhost"); //プロパティを閉じる cy.closeProperty(); - // task 2つ目 + //task 2つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); - cy.createShellScript(fileNameTask2, codeTask2); - - //script でシェルファイル選択 - cy.selectDropdownOptionExactScoped(scriptEle, 0, fileNameTask2); - - //ローカルホスト選択 - cy.selectDropdownOptionExactScoped(hostEle, 0, "localhost"); - - // ためし - cy.enterInputOrOutputFile(TYPE_INPUT, "message.txt", true, true); + //シェル作成 + cy.setupTaskWithScriptAndIO(fileNameTask2, codeTask2, TYPE_INPUT, "message.txt", "localhost"); //プロパティを閉じる cy.closeProperty(); - + //コンポーネント同士を接続 - cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); + cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); //待機 cy.wait(300); From de4a183732c8cc496a4a0e520fe01f132f3fce0e Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Tue, 17 Feb 2026 14:17:22 +0900 Subject: [PATCH 05/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 既存コマンドで対応可能な操作を差し替え --- test/cypress/e2e/jobExecute.cy.js | 84 ++++++++++++++----------------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index c7871dfa..18dd02d6 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -8,12 +8,48 @@ describe("jobExecute", ()=>{ return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } - //仮実装中なのでコマンドもここに書いておく + const animationWaitTime = 500; + + //open file editer fixed + Cypress.Commands.add("clickFileEditerFixed", ()=>{ + cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() + .wait(animationWaitTime); + }); + + //edit script fixed + Cypress.Commands.add("scriptEditFixed", (scriptName, script)=>{ + cy.contains(scriptName).click(); + cy.clickFileEditerFixed(); + cy.get("#editor").find("textarea") + .type(script, { force: true }); + //閉じるボタン + cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); + //変更内容を保存 + cy.contains("button", /^keep changes$/i) + .scrollIntoView() + .should("be.visible") + .and("not.be.disabled") + .click() + .wait(animationWaitTime); + }); + + //make script fixed + Cypress.Commands.add("scriptMakeFixed", (scriptName, script)=>{ + cy.clickFilesTab(); + cy.fileFolderMake("file", scriptName); + + cy.scriptEditFixed(scriptName, script); + cy.clickFilesTab(); + }); + Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target)=>{ const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; const hostEle = "[data-cy=\"component_property-host-select\"]"; - cy.createShellScript(scriptName, shellText); + cy.scriptMakeFixed(scriptName, shellText); + + //保存確認 + cy.waitForSnackbar(new RegExp(`${escapeRegExp(filename)}\\s+saved\\s*$`, "i")); //script でシェルファイル選択 cy.selectValueFromDropdownList(scriptEle, 3, scriptName); @@ -32,50 +68,6 @@ describe("jobExecute", ()=>{ .should("be.visible"); }); - Cypress.Commands.add( - "createShellScript", - (filename, text)=>{ - //シェル作成 - Files展開 - cy.get("[data-cy=\"component_property-files-panel_title\"]") - .scrollIntoView() - .click(); - //シェル作成 - New File クリック - cy.get("[data-cy=\"file_browser-new_file-btn\"]") - .scrollIntoView() - .click(); - //シェル作成 - ファイル名入力 - cy.get("[data-cy=\"file_browser-input-text_field\"]").type(filename); - //シェル作成 - OK押下 - cy.get("[data-cy=\"versatile_dialog_Create_new_File-ok-btn\"]").click(); - //シェル作成 - 選択 - cy.contains(".v-list-item__content", filename) - .scrollIntoView() - .should("be.visible") - .click(); - //シェル作成 - edit - cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click(); - //シェル作成 - 実行内容入力 - cy.get("#editor") - .should("have.class", "ace_editor") - .click() - .find("textarea.ace_text-input") - .should("exist") - .type("{selectall}{backspace}", { force: true }) - .type(text, { force: true }); - //シェル作成 - 閉じるボタン - cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); - //シェル作成 - 変更内容を保存 - cy.contains("button", /^keep changes$/i) - .scrollIntoView() - .should("be.visible") - .and("not.be.disabled") - .click(); - - //保存確認 - cy.waitForSnackbar(new RegExp(`${escapeRegExp(filename)}\\s+saved\\s*$`, "i")); - } - ); - const TYPE_INPUT = "input"; const TYPE_OUTPUT = "output"; const DEF_COMPONENT_TASK = "task"; From 21fe45c01e7fdf4deb5440db64c2e4657a689f9f Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Tue, 17 Feb 2026 14:31:33 +0900 Subject: [PATCH 06/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jobExecute独自コマンドをcommands-jobExecute.jsに切り出し --- test/cypress/e2e/jobExecute.cy.js | 110 +------------------- test/cypress/support/commands-jobExecute.js | 104 ++++++++++++++++++ test/cypress/support/e2e.js | 1 + 3 files changed, 107 insertions(+), 108 deletions(-) create mode 100644 test/cypress/support/commands-jobExecute.js diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 18dd02d6..34c11f24 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -1,73 +1,4 @@ describe("jobExecute", ()=>{ - /** - * エスケープ - * @param {string} s - 対象文字列 - * @returns {string} 修正文字列 - */ - function escapeRegExp(s) { - return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - } - - const animationWaitTime = 500; - - //open file editer fixed - Cypress.Commands.add("clickFileEditerFixed", ()=>{ - cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() - .wait(animationWaitTime); - }); - - //edit script fixed - Cypress.Commands.add("scriptEditFixed", (scriptName, script)=>{ - cy.contains(scriptName).click(); - cy.clickFileEditerFixed(); - cy.get("#editor").find("textarea") - .type(script, { force: true }); - //閉じるボタン - cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); - //変更内容を保存 - cy.contains("button", /^keep changes$/i) - .scrollIntoView() - .should("be.visible") - .and("not.be.disabled") - .click() - .wait(animationWaitTime); - }); - - //make script fixed - Cypress.Commands.add("scriptMakeFixed", (scriptName, script)=>{ - cy.clickFilesTab(); - cy.fileFolderMake("file", scriptName); - - cy.scriptEditFixed(scriptName, script); - cy.clickFilesTab(); - }); - - Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target)=>{ - const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; - const hostEle = "[data-cy=\"component_property-host-select\"]"; - - cy.scriptMakeFixed(scriptName, shellText); - - //保存確認 - cy.waitForSnackbar(new RegExp(`${escapeRegExp(filename)}\\s+saved\\s*$`, "i")); - - //script でシェルファイル選択 - cy.selectValueFromDropdownList(scriptEle, 3, scriptName); - - //ローカルホスト選択 - cy.selectValueFromDropdownList(hostEle, 3, target); - - //インプットファイル指定 - cy.enterInputOrOutputFile(inputType, ioFileName, true, true); - }); - - Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ - const timeout = options.timeout ?? 15000; - return cy - .contains("div.v-snackbar__content", messageRe, { timeout }) - .should("be.visible"); - }); - const TYPE_INPUT = "input"; const TYPE_OUTPUT = "output"; const DEF_COMPONENT_TASK = "task"; @@ -89,38 +20,6 @@ describe("jobExecute", ()=>{ return cy.removeAllProjects(); }); - /** - テストで使用するremotehost情報の設定 - 事前に設定しておけばよいため自動化は不要の可能性あり - */ - it.skip("リモートホスト設定作成", ()=>{ - const LABEL = Math.random().toString(36) - .substring(2, 10); - const HOST_NAME = "wheel_release_test_server"; - const PORT_NUMBER = 22; - const TEST_USER = "testuser"; - - cy.get("[data-cy=\"tool_bar-navi-icon\"]").click(); - cy.get("[data-cy=\"navigation-manage_remote_host-btn\"]").click(); - cy.get("[data-cy=\"remotehost-new_remote_host_setting-btn\"]").click(); - cy.get("[data-cy=\"add_new_host-label-text_field\"]").type(LABEL); - cy.get("[data-cy=\"add_new_host-hostname-text_field\"]").type(HOST_NAME); - cy.get("[data-cy=\"add_new_host-port_number_label-text_field\"]").type(`{selectall}{backspace}${PORT_NUMBER}`); - cy.get("[data-cy=\"add_new_host-user_id-text_field\"]").type(TEST_USER); - //Click on dialog title to trigger blur from all fields - cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").click(); - //Wait for OK button to be enabled - cy.get("[data-cy=\"add_new_host-ok-btn\"]", { timeout: 1000 }).should("not.be.disabled") - .click(); - cy.contains("tr", LABEL).find("[data-cy=\"action_row-edit-btn\"]") - .click(); - cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").should("be.visible"); - //ダイアログ内のテキスト確認 - cy.get("[data-cy=\"add_new_host-label-text_field\"]").find("input") - .should("have.value", LABEL); - //ダイアログ内のOKボタン - cy.get("[data-cy=\"add_new_host-ok-btn\"]").click(); - }); /** * localhostでのタスク実行 @@ -143,7 +42,7 @@ describe("jobExecute", ()=>{ .type(1); cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") .eq(1) - .click(); //Add input file button + .click(); //プロパティを閉じる cy.closeProperty(); @@ -179,11 +78,6 @@ describe("jobExecute", ()=>{ cy.get("[data-cy=\"workflow-play-btn\"]").click(); //完了まち - cy.get("[data-cy=\"workflow-project_state-btn\"]", { timeout: 30000 }) - .should(($el)=>{ - const text = $el.text().trim() - .replace(/\s+/g, " "); - expect(text).to.eq("status: finished"); - }); + cy.checkProjectStatus("finished"); }); }); diff --git a/test/cypress/support/commands-jobExecute.js b/test/cypress/support/commands-jobExecute.js new file mode 100644 index 00000000..e1cbbf06 --- /dev/null +++ b/test/cypress/support/commands-jobExecute.js @@ -0,0 +1,104 @@ +/** + * エスケープ + * @param {string} s - 対象文字列 + * @returns {string} 修正文字列 + */ +function escapeRegExp(s) { + return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** + * タスクにスクリプトファイルを作成 i/oファイルにセット + */ +Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target) => { + const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; + const hostEle = "[data-cy=\"component_property-host-select\"]"; + + //シェル作成 + cy.scriptMakeFixed(scriptName, shellText); + + //保存確認 + cy.waitForSnackbar(new RegExp(`${escapeRegExp(scriptName)}\\s+saved\\s*$`, "i")); + + //script でシェル選択 + cy.selectValueFromDropdownList(scriptEle, 3, scriptName); + + //ローカルホスト選択 + cy.selectValueFromDropdownList(hostEle, 3, target); + + //インプットファイル指定 + cy.enterInputOrOutputFile(inputType, ioFileName, true, true); +}); + +Cypress.Commands.add("waitForSnackbar", (messageRe, options = {}) => { + const timeout = options.timeout ?? 15000; + return cy + .contains("div.v-snackbar__content", messageRe, { timeout }) + .should("be.visible"); +}); + +/** + * テストで使用するremotehost情報の設定 + */ +Cypress.Commands.add("setupRemotehost", () => { + const LABEL = "wheel_release_test_server"; + const HOST_NAME = "wheel_release_test_server"; + const PORT_NUMBER = 22; + const TEST_USER = "testuser"; + + cy.get("[data-cy=\"tool_bar-navi-icon\"]").click(); + cy.get("[data-cy=\"navigation-manage_remote_host-btn\"]").click(); + cy.get("[data-cy=\"remotehost-new_remote_host_setting-btn\"]").click(); + cy.get("[data-cy=\"add_new_host-label-text_field\"]").type(LABEL); + cy.get("[data-cy=\"add_new_host-hostname-text_field\"]").type(HOST_NAME); + cy.get("[data-cy=\"add_new_host-port_number_label-text_field\"]").type(`{selectall}{backspace}${PORT_NUMBER}`); + cy.get("[data-cy=\"add_new_host-user_id-text_field\"]").type(TEST_USER); + //Click on dialog title to trigger blur from all fields + cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").click(); + //Wait for OK button to be enabled + cy.get("[data-cy=\"add_new_host-ok-btn\"]", { timeout: 1000 }).should("not.be.disabled") + .click(); + cy.contains("tr", LABEL).find("[data-cy=\"action_row-edit-btn\"]") + .click(); + cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").should("be.visible"); + //ダイアログ内のテキスト確認 + cy.get("[data-cy=\"add_new_host-label-text_field\"]").find("input") + .should("have.value", LABEL); + //ダイアログ内のOKボタン + cy.get("[data-cy=\"add_new_host-ok-btn\"]").click(); + +}); + +const animationWaitTime = 500; + +//open file editer fixed +Cypress.Commands.add("clickFileEditerFixed", () => { + cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() + .wait(animationWaitTime); +}); + +//edit script fixed +Cypress.Commands.add("scriptEditFixed", (scriptName, script) => { + cy.contains(scriptName).click(); + cy.clickFileEditerFixed(); + cy.get("#editor").find("textarea") + .type(script, { force: true }); + //閉じるボタン + cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); + //変更内容を保存 + cy.contains("button", /^keep changes$/i) + .scrollIntoView() + .should("be.visible") + .and("not.be.disabled") + .click() + .wait(animationWaitTime); +}); + +//make script fixed +Cypress.Commands.add("scriptMakeFixed", (scriptName, script) => { + cy.clickFilesTab(); + cy.fileFolderMake("file", scriptName); + + cy.scriptEditFixed(scriptName, script); + cy.clickFilesTab(); +}); diff --git a/test/cypress/support/e2e.js b/test/cypress/support/e2e.js index 2ddf9078..f074e3af 100644 --- a/test/cypress/support/e2e.js +++ b/test/cypress/support/e2e.js @@ -20,6 +20,7 @@ import "./commands-home"; import "./commands-remoteHost"; import "./commands-workFlow"; import "./commands-shortcut"; +import "./commands-jobExecute"; import "cypress-real-events"; //Alternatively you can use CommonJS syntax: From 3b034383b0253c56f5975987674444eaa4285592 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Tue, 17 Feb 2026 16:40:42 +0900 Subject: [PATCH 07/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit リモートホスト向けのテストを追加 リモートホスト設定を追加 --- test/cypress/e2e/jobExecute.cy.js | 64 ++++++++-- test/cypress/support/commands-jobExecute.js | 133 ++++++++++---------- test/wheel_config/remotehost.json | 59 ++++++++- 3 files changed, 180 insertions(+), 76 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 34c11f24..7bab72bb 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -6,6 +6,9 @@ describe("jobExecute", ()=>{ const TASK_NAME_1 = "task1"; const DEF_COMPONENT_FOREACH = "foreach"; const FOREACH_NAME_0 = "foreach0"; + const HOST_NAME = "wheel_release_test_server"; + const FILE_NAME_TASK1 = "task1.sh"; + const FILE_NAME_TASK2 = "task2.sh"; before(()=>{ return cy.removeAllProjects(); @@ -20,15 +23,12 @@ describe("jobExecute", ()=>{ return cy.removeAllProjects(); }); - /** * localhostでのタスク実行 * 現状実行完了までを確認 * ファイルの生成の確認方法を検討中 */ it("localhostでのタスク実行", ()=>{ - const fileNameTask1 = "task1.sh"; - const fileNameTask2 = "task2.sh"; const codeTask1 = `echo "test" > message.txt`; const codeTask2 = `cat message.txt >/dev/null 2>&1`; @@ -47,14 +47,57 @@ describe("jobExecute", ()=>{ //プロパティを閉じる cy.closeProperty(); - //foreach作成 + //foreach選択 cy.doubleClickComponentName(FOREACH_NAME_0); //task 1つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); //シェル作成 - cy.setupTaskWithScriptAndIO(fileNameTask1, codeTask1, TYPE_OUTPUT, "message.txt", "localhost"); + cy.setupTaskWithScriptAndIO(FILE_NAME_TASK1, codeTask1, TYPE_OUTPUT, "message.txt", "localhost"); + + //プロパティを閉じる + cy.closeProperty(); + + //task 2つ目 + cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); + + //シェル作成 + cy.setupTaskWithScriptAndIO(FILE_NAME_TASK2, codeTask2, TYPE_INPUT, "message.txt", "localhost"); + + //プロパティを閉じる + cy.closeProperty(); + + //コンポーネント同士を接続 + cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); + + //待機 + cy.wait(300); + + //タスク実行 + cy.get("[data-cy=\"workflow-play-btn\"]").click(); + + //完了まで待機 + cy.checkProjectStatus("finished"); + }); + + /** + * localhostでのタスク実行 + * 現状実行完了までを確認 + * ファイルの生成の確認方法を検討中 + */ + it("remoteHostでのタスク実行", ()=>{ + const codeTask1 = `echo "test" > message.txt`; + const codeTask2 = `cat message.txt >/dev/null 2>&1`; + + //task 1つ目 + cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); + + //シェル作成 + cy.setupTaskWithScriptAndIO(FILE_NAME_TASK1, codeTask1, TYPE_OUTPUT, "message.txt", HOST_NAME); + + //jobスケジューラ有効化 + cy.switchUseJobScheduler("on"); //プロパティを閉じる cy.closeProperty(); @@ -63,7 +106,10 @@ describe("jobExecute", ()=>{ cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); //シェル作成 - cy.setupTaskWithScriptAndIO(fileNameTask2, codeTask2, TYPE_INPUT, "message.txt", "localhost"); + cy.setupTaskWithScriptAndIO(FILE_NAME_TASK2, codeTask2, TYPE_INPUT, "message.txt", HOST_NAME); + + //jobスケジューラ有効化 + cy.switchUseJobScheduler("on"); //プロパティを閉じる cy.closeProperty(); @@ -77,7 +123,11 @@ describe("jobExecute", ()=>{ //タスク実行 cy.get("[data-cy=\"workflow-play-btn\"]").click(); - //完了まち + //リモートアクセスパスワード + cy.wait(300); + cy.passwordType("passw0rd"); + + //完了まで待機 cy.checkProjectStatus("finished"); }); }); diff --git a/test/cypress/support/commands-jobExecute.js b/test/cypress/support/commands-jobExecute.js index e1cbbf06..536106ef 100644 --- a/test/cypress/support/commands-jobExecute.js +++ b/test/cypress/support/commands-jobExecute.js @@ -4,101 +4,98 @@ * @returns {string} 修正文字列 */ function escapeRegExp(s) { - return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } /** * タスクにスクリプトファイルを作成 i/oファイルにセット */ -Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target) => { - const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; - const hostEle = "[data-cy=\"component_property-host-select\"]"; - - //シェル作成 - cy.scriptMakeFixed(scriptName, shellText); +Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target)=>{ + const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; + const hostEle = "[data-cy=\"component_property-host-select\"]"; - //保存確認 - cy.waitForSnackbar(new RegExp(`${escapeRegExp(scriptName)}\\s+saved\\s*$`, "i")); + //シェル作成 + cy.scriptMakeFixed(scriptName, shellText); - //script でシェル選択 - cy.selectValueFromDropdownList(scriptEle, 3, scriptName); + //script でシェル選択 + cy.selectValueFromDropdownList(scriptEle, 3, scriptName); - //ローカルホスト選択 - cy.selectValueFromDropdownList(hostEle, 3, target); + //ローカルホスト選択 + cy.selectValueFromDropdownList(hostEle, 3, target); - //インプットファイル指定 - cy.enterInputOrOutputFile(inputType, ioFileName, true, true); + //インプットファイル指定 + cy.enterInputOrOutputFile(inputType, ioFileName, true, true); + + //保存確認 + cy.waitForSnackbar(new RegExp(`${escapeRegExp(scriptName)}\\s+saved\\s*$`, "i")); }); -Cypress.Commands.add("waitForSnackbar", (messageRe, options = {}) => { - const timeout = options.timeout ?? 15000; - return cy - .contains("div.v-snackbar__content", messageRe, { timeout }) - .should("be.visible"); +Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ + const timeout = options.timeout ?? 15000; + return cy + .contains("div.v-snackbar__content", messageRe, { timeout }) + .should("be.visible"); }); /** * テストで使用するremotehost情報の設定 */ -Cypress.Commands.add("setupRemotehost", () => { - const LABEL = "wheel_release_test_server"; - const HOST_NAME = "wheel_release_test_server"; - const PORT_NUMBER = 22; - const TEST_USER = "testuser"; - - cy.get("[data-cy=\"tool_bar-navi-icon\"]").click(); - cy.get("[data-cy=\"navigation-manage_remote_host-btn\"]").click(); - cy.get("[data-cy=\"remotehost-new_remote_host_setting-btn\"]").click(); - cy.get("[data-cy=\"add_new_host-label-text_field\"]").type(LABEL); - cy.get("[data-cy=\"add_new_host-hostname-text_field\"]").type(HOST_NAME); - cy.get("[data-cy=\"add_new_host-port_number_label-text_field\"]").type(`{selectall}{backspace}${PORT_NUMBER}`); - cy.get("[data-cy=\"add_new_host-user_id-text_field\"]").type(TEST_USER); - //Click on dialog title to trigger blur from all fields - cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").click(); - //Wait for OK button to be enabled - cy.get("[data-cy=\"add_new_host-ok-btn\"]", { timeout: 1000 }).should("not.be.disabled") - .click(); - cy.contains("tr", LABEL).find("[data-cy=\"action_row-edit-btn\"]") - .click(); - cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").should("be.visible"); - //ダイアログ内のテキスト確認 - cy.get("[data-cy=\"add_new_host-label-text_field\"]").find("input") - .should("have.value", LABEL); - //ダイアログ内のOKボタン - cy.get("[data-cy=\"add_new_host-ok-btn\"]").click(); +Cypress.Commands.add("setupRemotehost", (label, hostName)=>{ + const PORT_NUMBER = 22; + const TEST_USER = "testuser"; + cy.get("[data-cy=\"tool_bar-navi-icon\"]").click(); + cy.get("[data-cy=\"navigation-manage_remote_host-btn\"]").click(); + cy.get("[data-cy=\"remotehost-new_remote_host_setting-btn\"]").click(); + cy.get("[data-cy=\"add_new_host-label-text_field\"]").type(label); + cy.get("[data-cy=\"add_new_host-hostname-text_field\"]").type(hostName); + cy.get("[data-cy=\"add_new_host-port_number_label-text_field\"]").type(`{selectall}{backspace}${PORT_NUMBER}`); + cy.get("[data-cy=\"add_new_host-user_id-text_field\"]").type(TEST_USER); + //Click on dialog title to trigger blur from all fields + cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").click(); + //Wait for OK button to be enabled + cy.get("[data-cy=\"add_new_host-ok-btn\"]", { timeout: 1000 }).should("not.be.disabled") + .click(); + cy.contains("tr", label).find("[data-cy=\"action_row-edit-btn\"]") + .click(); + cy.get("[data-cy=\"add_new_host-add_new_host-card_title\"]").should("be.visible"); + //ダイアログ内のテキスト確認 + cy.get("[data-cy=\"add_new_host-label-text_field\"]").find("input") + .should("have.value", label); + //ダイアログ内のOKボタン + cy.get("[data-cy=\"add_new_host-ok-btn\"]").click(); }); const animationWaitTime = 500; //open file editer fixed -Cypress.Commands.add("clickFileEditerFixed", () => { - cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() - .wait(animationWaitTime); +Cypress.Commands.add("clickFileEditerFixed", ()=>{ + cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() + .wait(animationWaitTime); }); //edit script fixed -Cypress.Commands.add("scriptEditFixed", (scriptName, script) => { - cy.contains(scriptName).click(); - cy.clickFileEditerFixed(); - cy.get("#editor").find("textarea") - .type(script, { force: true }); - //閉じるボタン - cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); - //変更内容を保存 - cy.contains("button", /^keep changes$/i) - .scrollIntoView() - .should("be.visible") - .and("not.be.disabled") - .click() - .wait(animationWaitTime); +Cypress.Commands.add("scriptEditFixed", (scriptName, script)=>{ + cy.contains(scriptName).click(); + cy.clickFileEditerFixed(); + cy.get("#editor").find("textarea") + .type(script, { force: true }); + //閉じるボタン + cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); + //変更内容を保存 + cy.contains("button", /^keep changes$/i) + .scrollIntoView() + .should("be.visible") + .and("not.be.disabled") + .click() + .wait(animationWaitTime); }); //make script fixed -Cypress.Commands.add("scriptMakeFixed", (scriptName, script) => { - cy.clickFilesTab(); - cy.fileFolderMake("file", scriptName); +Cypress.Commands.add("scriptMakeFixed", (scriptName, script)=>{ + cy.clickFilesTab(); + cy.fileFolderMake("file", scriptName); - cy.scriptEditFixed(scriptName, script); - cy.clickFilesTab(); + cy.scriptEditFixed(scriptName, script); + cy.clickFilesTab(); }); diff --git a/test/wheel_config/remotehost.json b/test/wheel_config/remotehost.json index fe51488c..3e7e2a6c 100644 --- a/test/wheel_config/remotehost.json +++ b/test/wheel_config/remotehost.json @@ -1 +1,58 @@ -[] +[ + { + "name": "componentTestLabel", + "host": "TestHostname", + "port": 8000, + "user": "testUser", + "numJob": 5, + "jobScheduler": "PBSPro", + "useBulkjob": true, + "useStepjob": true, + "queue": "testQueues", + "sharedHost": "", + "sharedPath": "", + "renewInterval": 0, + "statusCheckInterval": 60, + "maxStatusCheckError": 10, + "rcfile": "/etc/profile", + "useGfarm": true, + "id": "545dd3e0-b2;6d-11f0-bbe8-272d67e9e112" + }, + { + "name": "testServer", + "host": "wheel_release_test_server", + "port": 8000, + "numJob": 5, + "queue": "workq", + "jobScheduler": "PBSPro", + "useBulkjob": false, + "useStepjob": false, + "sharedHost": "", + "sharedPath": "", + "renewInterval": 0, + "statusCheckInterval": 60, + "maxStatusCheckError": 10, + "id": "91041660-c8a4-11ee-8b76-87edc1fbf31a", + "user": "testuser", + "rcfile": "/etc/profile", + "prependCmd": "" + }, + { + "name": "wheel_release_test_server", + "host": "wheel_release_test_server", + "port": 22, + "user": "testuser", + "id": "dd453fa0-0bc2-11f1-aec1-1f504ee4905a", + "numJob": 5, + "useBulkjob": false, + "useStepjob": false, + "sharedHost": "", + "sharedPath": "", + "renewInterval": 0, + "statusCheckInterval": 60, + "maxStatusCheckError": 10, + "rcfile": "/etc/profile", + "useGfarm": false, + "jobScheduler": "PBSPro" + } +] From 862f275b82c672d9826cc317a5fd6b1f04ae7df2 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Tue, 17 Feb 2026 16:56:09 +0900 Subject: [PATCH 08/14] test(cypress): fix e2e test commands.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 既存コマンドを現状のUIにあわせて修正 --- test/cypress/support/commands-jobExecute.js | 36 +-------------------- test/cypress/support/commands.js | 10 +++--- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/test/cypress/support/commands-jobExecute.js b/test/cypress/support/commands-jobExecute.js index 536106ef..3e7c928e 100644 --- a/test/cypress/support/commands-jobExecute.js +++ b/test/cypress/support/commands-jobExecute.js @@ -15,7 +15,7 @@ Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputTy const hostEle = "[data-cy=\"component_property-host-select\"]"; //シェル作成 - cy.scriptMakeFixed(scriptName, shellText); + cy.scriptMake(scriptName, shellText); //script でシェル選択 cy.selectValueFromDropdownList(scriptEle, 3, scriptName); @@ -65,37 +65,3 @@ Cypress.Commands.add("setupRemotehost", (label, hostName)=>{ //ダイアログ内のOKボタン cy.get("[data-cy=\"add_new_host-ok-btn\"]").click(); }); - -const animationWaitTime = 500; - -//open file editer fixed -Cypress.Commands.add("clickFileEditerFixed", ()=>{ - cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() - .wait(animationWaitTime); -}); - -//edit script fixed -Cypress.Commands.add("scriptEditFixed", (scriptName, script)=>{ - cy.contains(scriptName).click(); - cy.clickFileEditerFixed(); - cy.get("#editor").find("textarea") - .type(script, { force: true }); - //閉じるボタン - cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); - //変更内容を保存 - cy.contains("button", /^keep changes$/i) - .scrollIntoView() - .should("be.visible") - .and("not.be.disabled") - .click() - .wait(animationWaitTime); -}); - -//make script fixed -Cypress.Commands.add("scriptMakeFixed", (scriptName, script)=>{ - cy.clickFilesTab(); - cy.fileFolderMake("file", scriptName); - - cy.scriptEditFixed(scriptName, script); - cy.clickFilesTab(); -}); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 874d0b6c..90958699 100644 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -473,7 +473,7 @@ Cypress.Commands.add("swicthUseJavascriptExpressionForConditionCheck", (flg)=>{ //open file editer Cypress.Commands.add("clickFileEditer", ()=>{ - cy.get("[href=\"/editor\"]").click() + cy.get("[data-cy=\"file_browser-edit_files-btn\"]").click() .wait(animationWaitTime); }); @@ -488,9 +488,9 @@ Cypress.Commands.add("scriptEdit", (scriptName, script)=>{ cy.clickFileEditer(); cy.get("#editor").find("textarea") .type(script, { force: true }); - cy.get("button").contains("button", "save all files") - .click(); - cy.get("[href=\"/graph\"]", { timeout: 5500 }).click() + cy.get("[data-cy=\"workflow-text_editor_close-btn\"]").click(); + cy.get("button").contains(/^keep changes$/i) + .click() .wait(animationWaitTime); }); @@ -562,7 +562,7 @@ Cypress.Commands.add("resetProject", ()=>{ //input remotehost password Cypress.Commands.add("passwordType", (password)=>{ - cy.contains("input password or passphrase").parent() + cy.contains("input password for").parent() .parent() .next() .find("input") From 7a681f65463719557bcc5755686e5520597ecbb0 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Wed, 18 Feb 2026 14:44:30 +0900 Subject: [PATCH 09/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ローカルホスト向けのテストを改修 リモートホスト向けのテストを改修 --- test/cypress/e2e/jobExecute.cy.js | 105 ++++++++++---------- test/cypress/support/commands-jobExecute.js | 22 +++- 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 7bab72bb..16855c76 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -1,41 +1,56 @@ -describe("jobExecute", ()=>{ +describe("jobExecute", () => { const TYPE_INPUT = "input"; const TYPE_OUTPUT = "output"; const DEF_COMPONENT_TASK = "task"; + const DEF_COMPONENT_FOREACH = "foreach"; + const DEF_COMPONENT_WHILE = "while"; + const DEF_COMPONENT_WORKFLOW = "workflow"; const TASK_NAME_0 = "task0"; const TASK_NAME_1 = "task1"; - const DEF_COMPONENT_FOREACH = "foreach"; + const FOREACH_NAME_0 = "foreach0"; + const WORKFLOW_NAME_0 = "workflow0"; + const WHILE_NAME_0 = "while0"; + const LOCAL_HOST = "localhost"; const HOST_NAME = "wheel_release_test_server"; const FILE_NAME_TASK1 = "task1.sh"; const FILE_NAME_TASK2 = "task2.sh"; + const FILE_NAME_WHILE = "while.sh"; - before(()=>{ + const codeTask1 = `echo "test" > message.txt`; + const codeTask2 = `cat message.txt >/dev/null 2>&1`; + const codeWhile = [`set -eu`,`cnt=$(cat counter.txt)`,`if [ "$cnt" -lt 10 ]; then`,` exit 0`,`else`,` exit 1`].join('\n'); + + before(() => { return cy.removeAllProjects(); }); - beforeEach(()=>{ + beforeEach(() => { cy.viewport("macbook-16"); return cy.createAndOpenProject(); }); - after(()=>{ + after(() => { return cy.removeAllProjects(); }); /** * localhostでのタスク実行 - * 現状実行完了までを確認 - * ファイルの生成の確認方法を検討中 */ - it("localhostでのタスク実行", ()=>{ - const codeTask1 = `echo "test" > message.txt`; - const codeTask2 = `cat message.txt >/dev/null 2>&1`; + it("executeLocalhost", () => { + //workflow作成 + cy.createComponent(DEF_COMPONENT_WORKFLOW, WORKFLOW_NAME_0, 501, 500); + //while 作成 + cy.createComponent(DEF_COMPONENT_WHILE, WHILE_NAME_0, 501, 700); + cy.setupWhileWithScriptAndCondition(FILE_NAME_WHILE, codeWhile); + cy.closeProperty(); + //コンポーネント同士を接続 + cy.connectComponentMultiple(WORKFLOW_NAME_0, WHILE_NAME_0); + //workflow選択 + cy.doubleClickComponentName(WORKFLOW_NAME_0); //foreach作成 cy.createComponent(DEF_COMPONENT_FOREACH, FOREACH_NAME_0, 501, 500); - - //loop設定 cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() .click(); cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") @@ -43,35 +58,19 @@ describe("jobExecute", ()=>{ cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") .eq(1) .click(); - - //プロパティを閉じる cy.closeProperty(); - //foreach選択 cy.doubleClickComponentName(FOREACH_NAME_0); - //task 1つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); - - //シェル作成 - cy.setupTaskWithScriptAndIO(FILE_NAME_TASK1, codeTask1, TYPE_OUTPUT, "message.txt", "localhost"); - - //プロパティを閉じる + cy.setupTaskWithScriptAndIO(FILE_NAME_TASK1, codeTask1, TYPE_OUTPUT, "message.txt", LOCAL_HOST); cy.closeProperty(); - //task 2つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); - - //シェル作成 - cy.setupTaskWithScriptAndIO(FILE_NAME_TASK2, codeTask2, TYPE_INPUT, "message.txt", "localhost"); - - //プロパティを閉じる + cy.setupTaskWithScriptAndIO(FILE_NAME_TASK2, codeTask2, TYPE_INPUT, "message.txt", LOCAL_HOST); cy.closeProperty(); - //コンポーネント同士を接続 cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); - - //待機 cy.wait(300); //タスク実行 @@ -82,42 +81,44 @@ describe("jobExecute", ()=>{ }); /** - * localhostでのタスク実行 - * 現状実行完了までを確認 - * ファイルの生成の確認方法を検討中 + * remoteHostでのタスク実行 */ - it("remoteHostでのタスク実行", ()=>{ - const codeTask1 = `echo "test" > message.txt`; - const codeTask2 = `cat message.txt >/dev/null 2>&1`; + it("executeRemoteHost", () => { + //workflow作成 + cy.createComponent(DEF_COMPONENT_WORKFLOW, WORKFLOW_NAME_0, 501, 500); + //while 作成 + cy.createComponent(DEF_COMPONENT_WHILE, WHILE_NAME_0, 501, 700); + cy.setupWhileWithScriptAndCondition(FILE_NAME_WHILE, codeWhile); + cy.closeProperty(); + //コンポーネント同士を接続 + cy.connectComponentMultiple(WORKFLOW_NAME_0, WHILE_NAME_0); + //workflow選択 + cy.doubleClickComponentName(WORKFLOW_NAME_0); + //foreach作成 + cy.createComponent(DEF_COMPONENT_FOREACH, FOREACH_NAME_0, 501, 500); + cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() + .click(); + cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") + .type(1); + cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") + .eq(1) + .click(); + cy.closeProperty(); + //foreach選択 + cy.doubleClickComponentName(FOREACH_NAME_0); //task 1つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_0, 501, 500); - - //シェル作成 cy.setupTaskWithScriptAndIO(FILE_NAME_TASK1, codeTask1, TYPE_OUTPUT, "message.txt", HOST_NAME); - - //jobスケジューラ有効化 cy.switchUseJobScheduler("on"); - - //プロパティを閉じる cy.closeProperty(); - //task 2つ目 cy.createComponent(DEF_COMPONENT_TASK, TASK_NAME_1, 501, 700); - - //シェル作成 cy.setupTaskWithScriptAndIO(FILE_NAME_TASK2, codeTask2, TYPE_INPUT, "message.txt", HOST_NAME); - - //jobスケジューラ有効化 cy.switchUseJobScheduler("on"); - - //プロパティを閉じる cy.closeProperty(); - //コンポーネント同士を接続 cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); - - //待機 cy.wait(300); //タスク実行 diff --git a/test/cypress/support/commands-jobExecute.js b/test/cypress/support/commands-jobExecute.js index 3e7c928e..4f8011aa 100644 --- a/test/cypress/support/commands-jobExecute.js +++ b/test/cypress/support/commands-jobExecute.js @@ -6,11 +6,25 @@ function escapeRegExp(s) { return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } +/** + * whileにスクリプトファイルを作成 Conditionにセット + */ +Cypress.Commands.add("setupWhileWithScriptAndCondition", (scriptName, shellText) => { + const scriptEle = "[data-cy=\"component_property-condition_use_javascript-autocomplete\"]"; + //シェル作成 + cy.scriptMake(scriptName, shellText); + //Conditionをクリック + cy.get("[data-cy=\"component_property-condition-setting_title\"]").click(); + //Conditionにセット + cy.selectValueFromDropdownList(scriptEle, 3, scriptName); + //保存確認 + cy.waitForSnackbar(new RegExp(`${escapeRegExp(scriptName)}\\s+saved\\s*$`, "i")); +}); /** - * タスクにスクリプトファイルを作成 i/oファイルにセット + * taskにスクリプトファイルを作成 i/oファイルにセット */ -Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target)=>{ +Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target) => { const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; const hostEle = "[data-cy=\"component_property-host-select\"]"; @@ -30,7 +44,7 @@ Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputTy cy.waitForSnackbar(new RegExp(`${escapeRegExp(scriptName)}\\s+saved\\s*$`, "i")); }); -Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ +Cypress.Commands.add("waitForSnackbar", (messageRe, options = {}) => { const timeout = options.timeout ?? 15000; return cy .contains("div.v-snackbar__content", messageRe, { timeout }) @@ -40,7 +54,7 @@ Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ /** * テストで使用するremotehost情報の設定 */ -Cypress.Commands.add("setupRemotehost", (label, hostName)=>{ +Cypress.Commands.add("setupRemotehost", (label, hostName) => { const PORT_NUMBER = 22; const TEST_USER = "testuser"; From b02b4a710bf5c67f27b5ce33c07ad6f7999ecd9e Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Wed, 18 Feb 2026 15:36:35 +0900 Subject: [PATCH 10/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit フォーマッタ適用 foreachの繰り返し設定をコマンド化 --- test/cypress/e2e/jobExecute.cy.js | 31 ++++++--------------- test/cypress/support/commands-jobExecute.js | 28 ++++++++++++++++--- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 16855c76..50136a98 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -1,4 +1,4 @@ -describe("jobExecute", () => { +describe("jobExecute", ()=>{ const TYPE_INPUT = "input"; const TYPE_OUTPUT = "output"; const DEF_COMPONENT_TASK = "task"; @@ -19,25 +19,25 @@ describe("jobExecute", () => { const codeTask1 = `echo "test" > message.txt`; const codeTask2 = `cat message.txt >/dev/null 2>&1`; - const codeWhile = [`set -eu`,`cnt=$(cat counter.txt)`,`if [ "$cnt" -lt 10 ]; then`,` exit 0`,`else`,` exit 1`].join('\n'); + const codeWhile = [`set -eu`, `cnt=$(cat counter.txt)`, `if [ "$cnt" -lt 10 ]; then`, ` exit 0`, `else`, ` exit 1`].join("\n"); - before(() => { + before(()=>{ return cy.removeAllProjects(); }); - beforeEach(() => { + beforeEach(()=>{ cy.viewport("macbook-16"); return cy.createAndOpenProject(); }); - after(() => { + after(()=>{ return cy.removeAllProjects(); }); /** * localhostでのタスク実行 */ - it("executeLocalhost", () => { + it("executeLocalhost", ()=>{ //workflow作成 cy.createComponent(DEF_COMPONENT_WORKFLOW, WORKFLOW_NAME_0, 501, 500); //while 作成 @@ -51,13 +51,7 @@ describe("jobExecute", () => { //foreach作成 cy.createComponent(DEF_COMPONENT_FOREACH, FOREACH_NAME_0, 501, 500); - cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() - .click(); - cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") - .type(1); - cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") - .eq(1) - .click(); + cy.setForeachLoop(2); cy.closeProperty(); //foreach選択 cy.doubleClickComponentName(FOREACH_NAME_0); @@ -83,7 +77,7 @@ describe("jobExecute", () => { /** * remoteHostでのタスク実行 */ - it("executeRemoteHost", () => { + it("executeRemoteHost", ()=>{ //workflow作成 cy.createComponent(DEF_COMPONENT_WORKFLOW, WORKFLOW_NAME_0, 501, 500); //while 作成 @@ -94,16 +88,9 @@ describe("jobExecute", () => { cy.connectComponentMultiple(WORKFLOW_NAME_0, WHILE_NAME_0); //workflow選択 cy.doubleClickComponentName(WORKFLOW_NAME_0); - //foreach作成 cy.createComponent(DEF_COMPONENT_FOREACH, FOREACH_NAME_0, 501, 500); - cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() - .click(); - cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") - .type(1); - cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") - .eq(1) - .click(); + cy.setForeachLoop(2); cy.closeProperty(); //foreach選択 cy.doubleClickComponentName(FOREACH_NAME_0); diff --git a/test/cypress/support/commands-jobExecute.js b/test/cypress/support/commands-jobExecute.js index 4f8011aa..b47914dd 100644 --- a/test/cypress/support/commands-jobExecute.js +++ b/test/cypress/support/commands-jobExecute.js @@ -6,10 +6,11 @@ function escapeRegExp(s) { return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } + /** * whileにスクリプトファイルを作成 Conditionにセット */ -Cypress.Commands.add("setupWhileWithScriptAndCondition", (scriptName, shellText) => { +Cypress.Commands.add("setupWhileWithScriptAndCondition", (scriptName, shellText)=>{ const scriptEle = "[data-cy=\"component_property-condition_use_javascript-autocomplete\"]"; //シェル作成 cy.scriptMake(scriptName, shellText); @@ -24,7 +25,7 @@ Cypress.Commands.add("setupWhileWithScriptAndCondition", (scriptName, shellText) /** * taskにスクリプトファイルを作成 i/oファイルにセット */ -Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target) => { +Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputType, ioFileName, target)=>{ const scriptEle = "[data-cy=\"component_property-script-autocomplete\"]"; const hostEle = "[data-cy=\"component_property-host-select\"]"; @@ -44,17 +45,36 @@ Cypress.Commands.add("setupTaskWithScriptAndIO", (scriptName, shellText, inputTy cy.waitForSnackbar(new RegExp(`${escapeRegExp(scriptName)}\\s+saved\\s*$`, "i")); }); -Cypress.Commands.add("waitForSnackbar", (messageRe, options = {}) => { +/** + * ファイル保存待機 + */ +Cypress.Commands.add("waitForSnackbar", (messageRe, options = {})=>{ const timeout = options.timeout ?? 15000; return cy .contains("div.v-snackbar__content", messageRe, { timeout }) .should("be.visible"); }); +/** + * foreach 繰り返し設定 + */ +Cypress.Commands.add("setForeachLoop", (length)=>{ + cy.get("[data-cy=\"component_property-loop_set_foreach-panel_title\"]").scrollIntoView() + .click(); + + for (let index = 0; index < length; index++) { + cy.get("[data-cy=\"component_property-index_foreach-list_form\"]").find("input") + .type(index); + cy.get("[data-cy=\"list_form-add-text_field\"]").find("[role=\"button\"]") + .eq(1) + .click(); + } +}); + /** * テストで使用するremotehost情報の設定 */ -Cypress.Commands.add("setupRemotehost", (label, hostName) => { +Cypress.Commands.add("setupRemotehost", (label, hostName)=>{ const PORT_NUMBER = 22; const TEST_USER = "testuser"; From 8a35697253c21fb4a3a6907644e0ca0ccaac3324 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Wed, 18 Feb 2026 15:40:41 +0900 Subject: [PATCH 11/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit テスト説明のコメントを追加 --- test/cypress/e2e/jobExecute.cy.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index 50136a98..fba80cdd 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -1,3 +1,6 @@ +/** + * ジョブ実行テスト + */ describe("jobExecute", ()=>{ const TYPE_INPUT = "input"; const TYPE_OUTPUT = "output"; @@ -36,6 +39,8 @@ describe("jobExecute", ()=>{ /** * localhostでのタスク実行 + * 試験確認内容:ローカルホストに対するタスク実行ワークフローが + *        完了(status:finished)となること */ it("executeLocalhost", ()=>{ //workflow作成 @@ -76,6 +81,8 @@ describe("jobExecute", ()=>{ /** * remoteHostでのタスク実行 + * 試験確認内容:リモートホストに対するタスク実行ワークフローが + *        完了(status:finished)となること */ it("executeRemoteHost", ()=>{ //workflow作成 From e73ef74b7f6ce6cadd365ee88ec984d64021e80d Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Thu, 19 Feb 2026 13:31:32 +0900 Subject: [PATCH 12/14] test(cypress): fix e2e test jobexecute jobExecute.cy.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wait使用をやめ timeoutで表示を待つように変更 --- test/cypress/e2e/jobExecute.cy.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test/cypress/e2e/jobExecute.cy.js b/test/cypress/e2e/jobExecute.cy.js index fba80cdd..0c2a6cef 100644 --- a/test/cypress/e2e/jobExecute.cy.js +++ b/test/cypress/e2e/jobExecute.cy.js @@ -40,7 +40,7 @@ describe("jobExecute", ()=>{ /** * localhostでのタスク実行 * 試験確認内容:ローカルホストに対するタスク実行ワークフローが - *        完了(status:finished)となること + * 完了(status:finished)となること */ it("executeLocalhost", ()=>{ //workflow作成 @@ -70,10 +70,9 @@ describe("jobExecute", ()=>{ cy.closeProperty(); //コンポーネント同士を接続 cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); - cy.wait(300); //タスク実行 - cy.get("[data-cy=\"workflow-play-btn\"]").click(); + cy.get("[data-cy=\"workflow-play-btn\"]", { timeout: 3000 }).click(); //完了まで待機 cy.checkProjectStatus("finished"); @@ -82,7 +81,7 @@ describe("jobExecute", ()=>{ /** * remoteHostでのタスク実行 * 試験確認内容:リモートホストに対するタスク実行ワークフローが - *        完了(status:finished)となること + * 完了(status:finished)となること */ it("executeRemoteHost", ()=>{ //workflow作成 @@ -113,13 +112,12 @@ describe("jobExecute", ()=>{ cy.closeProperty(); //コンポーネント同士を接続 cy.connectComponentMultiple(TASK_NAME_0, TASK_NAME_1); - cy.wait(300); //タスク実行 - cy.get("[data-cy=\"workflow-play-btn\"]").click(); + cy.get("[data-cy=\"workflow-play-btn\"]", { timeout: 3000 }).click(); //リモートアクセスパスワード - cy.wait(300); + cy.get("[data-cy=\"buttons-ok-btn\"]", { timeout: 3000 }); cy.passwordType("passw0rd"); //完了まで待機 From 6c5af7aa244b0116872ebc4b312cb3bd8bf697f2 Mon Sep 17 00:00:00 2001 From: miyamori yuji Date: Thu, 19 Feb 2026 13:32:22 +0900 Subject: [PATCH 13/14] test(cypress): fix e2e test jobexecute commands-jobExecute.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit メソッドの説明を追記 --- test/cypress/support/commands-jobExecute.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cypress/support/commands-jobExecute.js b/test/cypress/support/commands-jobExecute.js index b47914dd..9287ec17 100644 --- a/test/cypress/support/commands-jobExecute.js +++ b/test/cypress/support/commands-jobExecute.js @@ -1,7 +1,7 @@ /** - * エスケープ + * 正規表現で使えるようにメタ文字をエスケープする * @param {string} s - 対象文字列 - * @returns {string} 修正文字列 + * @returns {string} エスケープ済み文字列 */ function escapeRegExp(s) { return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); From 066154b7a7c49d975d7f916fd4ae304fd44c56b2 Mon Sep 17 00:00:00 2001 From: "version-number-updater[bot]" Date: Mon, 2 Mar 2026 11:16:35 +0900 Subject: [PATCH 14/14] [skip ci] update version number --- server/app/db/version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app/db/version.json b/server/app/db/version.json index 255d4289..0735fb69 100644 --- a/server/app/db/version.json +++ b/server/app/db/version.json @@ -1 +1 @@ -{"version": "2026-0227-091935" } \ No newline at end of file +{"version": "2026-0302-111635" } \ No newline at end of file