From f706c6a463df821dc42f67e1e65209f5d402654b Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 5 Jul 2025 14:34:34 +0530 Subject: [PATCH 01/26] fix: menu bar hover border overlaps the tab bar active marker --- src/styles/Extn-TabBar.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index 3f3ba370df..413408c82e 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -5,7 +5,7 @@ border-bottom: none; position: relative; overflow: hidden; - z-index: 2; + z-index: 20; } .dark .tab-bar-container { From e0efc858ddaa41790c649ea2c167f5c6a811c2b6 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 5 Jul 2025 15:16:43 +0530 Subject: [PATCH 02/26] refactor: rename close unmodified tabs to close saved tabs for better readability --- src/extensionsIntegrated/TabBar/more-options.js | 14 +++++++------- src/nls/root/strings.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index a1b5a37d80..d8bbd41d91 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -37,8 +37,8 @@ define(function (require, exports, module) { Strings.CLOSE_TAB, Strings.CLOSE_TABS_TO_THE_LEFT, Strings.CLOSE_TABS_TO_THE_RIGHT, + Strings.CLOSE_SAVED_TABS, Strings.CLOSE_ALL_TABS, - Strings.CLOSE_UNMODIFIED_TABS, "---", Strings.CMD_FILE_RENAME, Strings.CMD_FILE_DELETE, @@ -89,12 +89,12 @@ define(function (require, exports, module) { } /** - * "CLOSE UNMODIFIED TABS" - * This will close all tabs that are not modified in the specified pane + * "CLOSE SAVED TABS" + * This will close all tabs that are not dirty in the specified pane * * @param {String} paneId - the id of the pane ["first-pane", "second-pane"] */ - function handleCloseUnmodifiedTabs(paneId) { + function handleCloseSavedTabs(paneId) { if (!paneId) { return; } @@ -108,7 +108,7 @@ define(function (require, exports, module) { // get all those entries that are not dirty const unmodifiedEntries = workingSet.filter((entry) => !entry.isDirty); - // close each unmodified file in the pane + // close each non-dirty file in the pane for (let i = unmodifiedEntries.length - 1; i >= 0; i--) { const fileObj = FileSystem.getFileForPath(unmodifiedEntries[i].path); CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); @@ -306,8 +306,8 @@ define(function (require, exports, module) { case Strings.CLOSE_ALL_TABS: handleCloseAllTabs(paneId); break; - case Strings.CLOSE_UNMODIFIED_TABS: - handleCloseUnmodifiedTabs(paneId); + case Strings.CLOSE_SAVED_TABS: + handleCloseSavedTabs(paneId); break; case Strings.CMD_FILE_RENAME: handleFileRename(filePath); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 7bb026591d..9d3514aa5b 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -434,7 +434,7 @@ define({ "CLOSE_TABS_TO_THE_RIGHT": "Close Tabs to the Right", "CLOSE_TABS_TO_THE_LEFT": "Close Tabs to the Left", "CLOSE_ALL_TABS": "Close All Tabs", - "CLOSE_UNMODIFIED_TABS": "Close Unmodified Tabs", + "CLOSE_SAVED_TABS": "Close Saved Tabs", "REOPEN_CLOSED_FILE": "Reopen Closed File", // CodeInspection: errors/warnings From 1f93ceb9e5066a12cfd6b636efba7f63dd3ab133 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 5 Jul 2025 17:42:48 +0530 Subject: [PATCH 03/26] fix: revert z-index from 20 to 2 --- src/styles/Extn-TabBar.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index 413408c82e..3f3ba370df 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -5,7 +5,7 @@ border-bottom: none; position: relative; overflow: hidden; - z-index: 20; + z-index: 2; } .dark .tab-bar-container { From 317a55d385d53cba241bfbb50646fa04e5305411 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 5 Jul 2025 22:07:49 +0530 Subject: [PATCH 04/26] feat: tab bar integ tests to check visibility of tab bar based on preferences --- test/UnitTestSuite.js | 1 + test/spec/Extn-Tabbar-integ-test.js | 97 +++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 test/spec/Extn-Tabbar-integ-test.js diff --git a/test/UnitTestSuite.js b/test/UnitTestSuite.js index 0afbbd1b16..8e48d70103 100644 --- a/test/UnitTestSuite.js +++ b/test/UnitTestSuite.js @@ -129,6 +129,7 @@ define(function (require, exports, module) { require("spec/Extn-ESLint-integ-test"); require("spec/Extn-CSSColorPreview-integ-test"); require("spec/Extn-CollapseFolders-integ-test"); + require("spec/Extn-Tabbar-integ-test"); // extension integration tests require("spec/Extn-CSSCodeHints-integ-test"); require("spec/Extn-HTMLCodeHints-Lint-integ-test"); diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js new file mode 100644 index 0000000000..405745f46d --- /dev/null +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -0,0 +1,97 @@ +/* + * GNU AGPL-3.0 License + * + * Copyright (c) 2021 - present core.ai . All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License + * for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0. + * + */ + +/*global describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, awaitsFor, awaitsForDone, awaits, jsPromise */ + +define(function (require, exports, module) { + const SpecRunnerUtils = require("spec/SpecRunnerUtils"); + + describe("integration:TabBar", function () { + let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, testFilePath; + + beforeAll(async function () { + // Create the test window + testWindow = await SpecRunnerUtils.createTestWindowAndRun(); + // Get reference to useful modules + $ = testWindow.$; + PreferencesManager = testWindow.brackets.test.PreferencesManager; + FileSystem = testWindow.brackets.test.FileSystem; + MainViewManager = testWindow.brackets.test.MainViewManager; + CommandManager = testWindow.brackets.test.CommandManager; + Commands = testWindow.brackets.test.Commands; + + // Create a test file + testFilePath = SpecRunnerUtils.getTempDirectory() + "/tabbar-test.js"; + await SpecRunnerUtils.createTempDirectory(); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath, "// Test file for TabBar", FileSystem)); + + // Open the test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + }, 30000); + + afterAll(async function () { + // Close the test file + await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); + + testWindow = null; + await SpecRunnerUtils.closeTestWindow(); + await SpecRunnerUtils.removeTempDirectory(); + }, 30000); + + describe("Visibility", function () { + it("should show tab bar when the feature is enabled", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Wait for the tab bar to become visible + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become visible", + 1000 + ); + + // Verify the tab bar is visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(true); + }); + + it("should hide tab bar when the feature is disabled", async function () { + // Disable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: false, numberOfTabs: -1 }); + + // Wait for the tab bar to become hidden + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become hidden", + 1000 + ); + + // Verify the tab bar is not visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + }); + }); + }); +}); From dc0e4822980891f22f051d2f0b7357651cafaedd Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 5 Jul 2025 23:09:04 +0530 Subject: [PATCH 05/26] feat: covered tests to check for add/remove tab based on working set --- test/spec/Extn-Tabbar-integ-test.js | 202 +++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 405745f46d..2b6b625a3e 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -25,11 +25,12 @@ define(function (require, exports, module) { describe("integration:TabBar", function () { let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, testFilePath; + let testFilePath2, testFilePath3; beforeAll(async function () { // Create the test window testWindow = await SpecRunnerUtils.createTestWindowAndRun(); - // Get reference to useful modules + // Get reference to all the required modules $ = testWindow.$; PreferencesManager = testWindow.brackets.test.PreferencesManager; FileSystem = testWindow.brackets.test.FileSystem; @@ -37,12 +38,17 @@ define(function (require, exports, module) { CommandManager = testWindow.brackets.test.CommandManager; Commands = testWindow.brackets.test.Commands; - // Create a test file + // Create test files testFilePath = SpecRunnerUtils.getTempDirectory() + "/tabbar-test.js"; + testFilePath2 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test2.js"; + testFilePath3 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test3.js"; + await SpecRunnerUtils.createTempDirectory(); - await jsPromise(SpecRunnerUtils.createTextFile(testFilePath, "// Test file for TabBar", FileSystem)); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath, "// Test file 1 for TabBar", FileSystem)); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath2, "// Test file 2 for TabBar", FileSystem)); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath3, "// Test file 3 for TabBar", FileSystem)); - // Open the test file + // Open the first test file await awaitsForDone( CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), "Open test file" @@ -50,7 +56,7 @@ define(function (require, exports, module) { }, 30000); afterAll(async function () { - // Close the test file + // Close all files await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); testWindow = null; @@ -58,6 +64,23 @@ define(function (require, exports, module) { await SpecRunnerUtils.removeTempDirectory(); }, 30000); + /** + * Helper function to check if a tab for a specific file exists in the tab bar + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab exists, false otherwise + */ + function tabExists(filePath) { + return $(`.tab[data-path="${filePath}"]`).length > 0; + } + + /** + * Helper function to count the number of tabs in the tab bar + * @returns {number} - The number of tabs + */ + function getTabCount() { + return $(".tab").length; + } + describe("Visibility", function () { it("should show tab bar when the feature is enabled", async function () { // Enable the tab bar feature @@ -93,5 +116,174 @@ define(function (require, exports, module) { expect($("#phoenix-tab-bar").is(":visible")).toBe(false); }); }); + + describe("Working Set", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); + + // Wait for the tab bar to update + await awaits(300); + }); + + it("should add tabs when files are added to the working set", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + + // Wait for the tab bar to update + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab for first file to appear", + 1000 + ); + + // Verify the tab exists + expect(tabExists(testFilePath)).toBe(true); + expect(getTabCount()).toBe(1); + + // Open the second test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + + // Wait for the tab bar to update + await awaitsFor( + function () { + return tabExists(testFilePath2); + }, + "Tab for second file to appear", + 1000 + ); + + // Verify both tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(getTabCount()).toBe(2); + + // Open the third test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for the tab bar to update + await awaitsFor( + function () { + return tabExists(testFilePath3); + }, + "Tab for third file to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(3); + }); + + it("should remove tabs when files are removed from the working set", async function () { + // Open all three test files like in previous test + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(getTabCount()).toBe(3); + + // Close the second test file + const fileToClose2 = FileSystem.getFileForPath(testFilePath2); + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose2 }), + "Close second test file" + ); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath2); + }, + "Tab for second file to disappear", + 1000 + ); + + // Verify the second tab is removed + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(2); + + // Close the first test file + const fileToClose1 = FileSystem.getFileForPath(testFilePath); + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose1 }), + "Close first test file" + ); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath); + }, + "Tab for first file to disappear", + 1000 + ); + + // Verify the first tab is removed + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(1); + + // Close the third test file + const fileToClose3 = FileSystem.getFileForPath(testFilePath3); + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose3 }), + "Close third test file" + ); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath3); + }, + "Tab for third file to disappear", + 1000 + ); + + // Verify all tabs are removed + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(0); + }); + }); }); }); From 012736c0e2b1f0bd48c5410be55a343b30e1b052 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 13:53:11 +0530 Subject: [PATCH 06/26] feat: add tests to test active tab functionality --- test/spec/Extn-Tabbar-integ-test.js | 132 ++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 2b6b625a3e..252f029089 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -81,6 +81,15 @@ define(function (require, exports, module) { return $(".tab").length; } + /** + * Helper function to check if a tab for a specific file is active + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab is active, false otherwise + */ + function isTabActive(filePath) { + return $(`.tab[data-path="${filePath}"].active`).length > 0; + } + describe("Visibility", function () { it("should show tab bar when the feature is enabled", async function () { // Enable the tab bar feature @@ -285,5 +294,128 @@ define(function (require, exports, module) { expect(getTabCount()).toBe(0); }); }); + + describe("Active Tab", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); + + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + }); + + it("should change active tab when switching files in the working set", async function () { + // Switch to the first file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Switch to first file" + ); + + // Wait for the tab to become active + await awaitsFor( + function () { + return isTabActive(testFilePath); + }, + "First tab to become active", + 1000 + ); + + // Verify the first tab is active and others are not + expect(isTabActive(testFilePath)).toBe(true); + expect(isTabActive(testFilePath2)).toBe(false); + expect(isTabActive(testFilePath3)).toBe(false); + + // Switch to the second file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Switch to second file" + ); + + // Wait for the tab to become active + await awaitsFor( + function () { + return isTabActive(testFilePath2); + }, + "Second tab to become active", + 1000 + ); + + // Verify the second tab is active and others are not + expect(isTabActive(testFilePath)).toBe(false); + expect(isTabActive(testFilePath2)).toBe(true); + expect(isTabActive(testFilePath3)).toBe(false); + + // Switch to the third file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Switch to third file" + ); + + // Wait for the tab to become active + await awaitsFor( + function () { + return isTabActive(testFilePath3); + }, + "Third tab to become active", + 1000 + ); + + // Verify the third tab is active and others are not + expect(isTabActive(testFilePath)).toBe(false); + expect(isTabActive(testFilePath2)).toBe(false); + expect(isTabActive(testFilePath3)).toBe(true); + }); + + it("should display active tab correctly based on the active file in the working set", async function () { + // Get the currently active file + const activeFile = MainViewManager.getCurrentlyViewedFile(); + + // just a small timer because tab bar gets recreated + await awaits(100); + + // Verify the tab for the active file is active + expect(isTabActive(activeFile.fullPath)).toBe(true); + + // Switch to a different file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Switch to second file" + ); + + // Get the new active file + const newActiveFile = MainViewManager.getCurrentlyViewedFile(); + + await awaits(100); + + // Verify the tab for the new active file is active + expect(isTabActive(newActiveFile.fullPath)).toBe(true); + + // Verify the tab for the previous active file is no longer active + expect(isTabActive(activeFile.fullPath)).toBe(false); + }); + }); }); }); From ab0c33fea652624fdd2bb6e87bb553a9345c896b Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 15:04:02 +0530 Subject: [PATCH 07/26] feat: tests to make sure all the items are appearing properly in the tab --- test/spec/Extn-Tabbar-integ-test.js | 465 +++++++++++++++++++++++++++- 1 file changed, 450 insertions(+), 15 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 252f029089..db6401f440 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -24,8 +24,8 @@ define(function (require, exports, module) { const SpecRunnerUtils = require("spec/SpecRunnerUtils"); describe("integration:TabBar", function () { - let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, testFilePath; - let testFilePath2, testFilePath3; + let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, DocumentManager; + let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; beforeAll(async function () { // Create the test window @@ -37,16 +37,39 @@ define(function (require, exports, module) { MainViewManager = testWindow.brackets.test.MainViewManager; CommandManager = testWindow.brackets.test.CommandManager; Commands = testWindow.brackets.test.Commands; + DocumentManager = testWindow.brackets.test.DocumentManager; // Create test files testFilePath = SpecRunnerUtils.getTempDirectory() + "/tabbar-test.js"; testFilePath2 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test2.js"; testFilePath3 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test3.js"; + // Create files with the same name in different directories for testing duplicate name handling + testDuplicateDir1 = SpecRunnerUtils.getTempDirectory() + "/dir1"; + testDuplicateDir2 = SpecRunnerUtils.getTempDirectory() + "/dir2"; + testDuplicateName = "duplicate.js"; + await SpecRunnerUtils.createTempDirectory(); + await SpecRunnerUtils.ensureExistsDirAsync(testDuplicateDir1); + await SpecRunnerUtils.ensureExistsDirAsync(testDuplicateDir2); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath, "// Test file 1 for TabBar", FileSystem)); await jsPromise(SpecRunnerUtils.createTextFile(testFilePath2, "// Test file 2 for TabBar", FileSystem)); await jsPromise(SpecRunnerUtils.createTextFile(testFilePath3, "// Test file 3 for TabBar", FileSystem)); + await jsPromise( + SpecRunnerUtils.createTextFile( + testDuplicateDir1 + "/" + testDuplicateName, + "// Duplicate file 1", + FileSystem + ) + ); + await jsPromise( + SpecRunnerUtils.createTextFile( + testDuplicateDir2 + "/" + testDuplicateName, + "// Duplicate file 2", + FileSystem + ) + ); // Open the first test file await awaitsForDone( @@ -56,8 +79,8 @@ define(function (require, exports, module) { }, 30000); afterAll(async function () { - // Close all files - await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); + // Close all files without prompting to save + await testWindow.closeAllFiles(); testWindow = null; await SpecRunnerUtils.closeTestWindow(); @@ -90,6 +113,88 @@ define(function (require, exports, module) { return $(`.tab[data-path="${filePath}"].active`).length > 0; } + /** + * Helper function to get the tab element for a specific file + * @param {string} filePath - The path of the file + * @returns {jQuery} - The tab element + */ + function getTab(filePath) { + return $(`.tab[data-path="${filePath}"]`); + } + + /** + * Helper function to check if a tab has a dirty indicator + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a dirty indicator, false otherwise + */ + function isTabDirty(filePath) { + return getTab(filePath).hasClass("dirty"); + } + + /** + * Helper function to get the tab name element for a specific file + * @param {string} filePath - The path of the file + * @returns {jQuery} - The tab name element + */ + function getTabName(filePath) { + return getTab(filePath).find(".tab-name"); + } + + /** + * Helper function to check if a tab has a directory name displayed + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a directory name, false otherwise + */ + function hasDirectoryName(filePath) { + return getTab(filePath).find(".tab-dirname").length > 0; + } + + /** + * Helper function to get the directory name displayed in a tab + * @param {string} filePath - The path of the file + * @returns {string} - The directory name or empty string if not found + */ + function getDirectoryName(filePath) { + const $dirName = getTab(filePath).find(".tab-dirname"); + return $dirName.length ? $dirName.text() : ""; + } + + /** + * Helper function to check if a tab has a file icon + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a file icon, false otherwise + */ + function hasFileIcon(filePath) { + return getTab(filePath).find(".tab-icon i").length > 0; + } + + /** + * Helper function to check if a tab has a git status indicator + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a git status indicator, false otherwise + */ + function hasGitStatus(filePath) { + return getTab(filePath).hasClass("git-new") || getTab(filePath).hasClass("git-modified"); + } + + /** + * Helper function to get the tooltip (title attribute) of a tab + * @param {string} filePath - The path of the file + * @returns {string} - The tooltip text + */ + function getTabTooltip(filePath) { + return getTab(filePath).attr("title") || ""; + } + + /** + * Helper function to check if a tab has a close button + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a close button, false otherwise + */ + function hasCloseButton(filePath) { + return getTab(filePath).find(".tab-close").length > 0; + } + describe("Visibility", function () { it("should show tab bar when the feature is enabled", async function () { // Enable the tab bar feature @@ -132,7 +237,7 @@ define(function (require, exports, module) { PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); // Close all files to start with a clean state - await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); + await testWindow.closeAllFiles(); // Wait for the tab bar to update await awaits(300); @@ -229,11 +334,16 @@ define(function (require, exports, module) { // Close the second test file const fileToClose2 = FileSystem.getFileForPath(testFilePath2); - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose2 }), - "Close second test file" + const promise2 = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose2 }); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE ); + await awaitsForDone(promise2, "Close second test file"); + // Wait for the tab to disappear await awaitsFor( function () { @@ -251,11 +361,16 @@ define(function (require, exports, module) { // Close the first test file const fileToClose1 = FileSystem.getFileForPath(testFilePath); - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose1 }), - "Close first test file" + const promise1 = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose1 }); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE ); + await awaitsForDone(promise1, "Close first test file"); + // Wait for the tab to disappear await awaitsFor( function () { @@ -273,11 +388,16 @@ define(function (require, exports, module) { // Close the third test file const fileToClose3 = FileSystem.getFileForPath(testFilePath3); - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose3 }), - "Close third test file" + const promise3 = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose3 }); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE ); + await awaitsForDone(promise3, "Close third test file"); + // Wait for the tab to disappear await awaitsFor( function () { @@ -301,7 +421,7 @@ define(function (require, exports, module) { PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); // Close all files to start with a clean state - await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL), "Close all files"); + await testWindow.closeAllFiles(); // Open all three test files await awaitsForDone( @@ -417,5 +537,320 @@ define(function (require, exports, module) { expect(isTabActive(activeFile.fullPath)).toBe(false); }); }); + + describe("Tab Items", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + }); + + it("should display the correct tab name", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Get the filename from the path + const fileName = testFilePath.split("/").pop(); + + // Verify the tab name is correct + expect(getTabName(testFilePath).text()).toBe(fileName); + }); + + it("should display a file icon", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Verify the tab has a file icon + expect(hasFileIcon(testFilePath)).toBe(true); + }); + + it("should display a dirty indicator when the file is modified and remove it when saved", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Initially, the file should not be dirty + expect(isTabDirty(testFilePath)).toBe(false); + + // Get the document and modify it + const doc = DocumentManager.getOpenDocumentForPath(testFilePath); + doc.setText("// Modified content"); + + // Wait for the dirty indicator to appear + await awaitsFor( + function () { + return isTabDirty(testFilePath); + }, + "Dirty indicator to appear", + 1000 + ); + + // Verify the tab has a dirty indicator + expect(isTabDirty(testFilePath)).toBe(true); + + // Save the file + await awaitsForDone(CommandManager.execute(Commands.FILE_SAVE, { doc: doc }), "Save file"); + + // Wait for the dirty indicator to disappear + await awaitsFor( + function () { + return !isTabDirty(testFilePath); + }, + "Dirty indicator to disappear", + 1000 + ); + + // Verify the tab no longer has a dirty indicator + expect(isTabDirty(testFilePath)).toBe(false); + + // Revert the changes for cleanup + doc.setText("// Test file 1 for TabBar"); + await awaitsForDone( + CommandManager.execute(Commands.FILE_SAVE, { doc: doc }), + "Save file with original content" + ); + }); + + it("should display directory name for files with the same name", async function () { + // Open both duplicate files + const duplicateFile1 = testDuplicateDir1 + "/" + testDuplicateName; + const duplicateFile2 = testDuplicateDir2 + "/" + testDuplicateName; + + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: duplicateFile1 }), + "Open first duplicate file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: duplicateFile2 }), + "Open second duplicate file" + ); + + // Wait for both tabs to appear + await awaitsFor( + function () { + return tabExists(duplicateFile1) && tabExists(duplicateFile2); + }, + "Both duplicate tabs to appear", + 1000 + ); + + // Verify both tabs have directory names + expect(hasDirectoryName(duplicateFile1)).toBe(true); + expect(hasDirectoryName(duplicateFile2)).toBe(true); + + // Verify the directory names are correct + expect(getDirectoryName(duplicateFile1)).toContain("dir1"); + expect(getDirectoryName(duplicateFile2)).toContain("dir2"); + }); + + it("should display the full file path in the tooltip", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Verify the tooltip contains the full path + const tooltip = getTabTooltip(testFilePath); + expect(tooltip).toContain(Phoenix.app.getDisplayPath(testFilePath)); + }); + + it("should display git change markers when git is enabled", async function () { + // Skip this test if Git integration is not available + if (!testWindow.brackets.test.Phoenix || !testWindow.brackets.test.Phoenix.app) { + expect("Test skipped - Phoenix.app not available").toBe("Test skipped - Phoenix.app not available"); + return; + } + + // Create a mock for the Git integration + if (!testWindow.phoenixGitEvents) { + testWindow.phoenixGitEvents = {}; + } + + // Save the original Git integration if it exists + const originalGitEvents = testWindow.phoenixGitEvents.TabBarIntegration; + + // Create a mock TabBarIntegration + testWindow.phoenixGitEvents.TabBarIntegration = { + isUntracked: function (path) { + return path === testFilePath; // Mark the first file as untracked + }, + isModified: function (path) { + return path === testFilePath2; // Mark the second file as modified + } + }; + + // Make sure the EventEmitter exists + if (!testWindow.phoenixGitEvents.EventEmitter) { + testWindow.phoenixGitEvents.EventEmitter = { + on: function () {}, + emit: function () {} + }; + } + + // Open the test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + + // Trigger a Git status update + testWindow.phoenixGitEvents.EventEmitter.emit("GIT_FILE_STATUS_CHANGED"); + + // Wait for the tabs to update + await awaits(300); + + // Verify the first file has the git-new class + const $tab1 = getTab(testFilePath); + expect($tab1.hasClass("git-new")).toBe(true); + expect(hasGitStatus(testFilePath)).toBe(true); + + // Verify the second file has the git-modified class + const $tab2 = getTab(testFilePath2); + expect($tab2.hasClass("git-modified")).toBe(true); + expect(hasGitStatus(testFilePath2)).toBe(true); + + // Verify the tooltips contain the Git status + expect(getTabTooltip(testFilePath)).toContain("Untracked"); + expect(getTabTooltip(testFilePath2)).toContain("Modified"); + + // Restore the original Git integration + testWindow.phoenixGitEvents.TabBarIntegration = originalGitEvents; + }); + + it("should display a close button", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Verify the tab has a close button + expect(hasCloseButton(testFilePath)).toBe(true); + + // Verify the close button has the correct icon + const $closeButton = getTab(testFilePath).find(".tab-close"); + expect($closeButton.find("i.fa-times").length).toBe(1); + }); + + it("should close the file when the close button is clicked", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Get the close button + const $closeButton = getTab(testFilePath).find(".tab-close"); + + // Create a spy for the FILE_CLOSE command + const executeOriginal = CommandManager.execute; + let fileCloseCalled = false; + let fileClosePathArg = null; + + CommandManager.execute = function (command, args) { + if (command === Commands.FILE_CLOSE) { + fileCloseCalled = true; + if (args && args.file) { + fileClosePathArg = args.file.fullPath; + } + } + return executeOriginal.apply(CommandManager, arguments); + }; + + // Click the close button + $closeButton.click(); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath); + }, + "Tab to disappear", + 1000 + ); + + // Restore the original execute function + CommandManager.execute = executeOriginal; + + // Verify the FILE_CLOSE command was called with the correct file + expect(fileCloseCalled).toBe(true); + expect(fileClosePathArg).toBe(testFilePath); + }); + }); }); }); From 85455585b860bd43ca7a9d59cd2acb766ad86736 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 18:17:50 +0530 Subject: [PATCH 08/26] feat: add test to make sure that tab clicks are working fine --- test/spec/Extn-Tabbar-integ-test.js | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index db6401f440..515a91c80b 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -536,6 +536,49 @@ define(function (require, exports, module) { // Verify the tab for the previous active file is no longer active expect(isTabActive(activeFile.fullPath)).toBe(false); }); + + it("should switch files properly when different tabs are clicked", async function () { + // Helper function to click a tab and verify it becomes active + async function clickTabAndVerify(filePath, description) { + const $tab = getTab(filePath); + expect($tab.length).toBe(1); + $tab.trigger("mousedown"); + + // Wait for the file to become active + await awaitsFor( + function () { + return ( + isTabActive(filePath) && MainViewManager.getCurrentlyViewedFile().fullPath === filePath + ); + }, + `${description} to become active after tab click`, + 1000 + ); + + // Verify this tab is active and others are not + const allPaths = [testFilePath, testFilePath2, testFilePath3]; + allPaths.forEach((path) => { + expect(isTabActive(path)).toBe(path === filePath); + }); + + // Verify the correct file is loaded in the editor + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(filePath); + } + // add a small timer to make sure that the tab bar is properly loaded + await awaits(100); + + // Initially, verify the third file is active (last opened) + expect(isTabActive(testFilePath3)).toBe(true); + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testFilePath3); + + // Test clicking on each tab + await clickTabAndVerify(testFilePath, "First file"); + await clickTabAndVerify(testFilePath2, "Second file"); + await clickTabAndVerify(testFilePath3, "Third file"); + + // Click back on the first tab to ensure it still works + await clickTabAndVerify(testFilePath, "First file"); + }); }); describe("Tab Items", function () { From 29ddc8609fa16ad8f29c7f0df9715993448ef5b0 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 18:59:11 +0530 Subject: [PATCH 09/26] feat: tests for overflow button and its dropdown menu --- test/spec/Extn-Tabbar-integ-test.js | 324 ++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 515a91c80b..aae452c393 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -195,6 +195,67 @@ define(function (require, exports, module) { return getTab(filePath).find(".tab-close").length > 0; } + /** + * Helper function to check if the overflow button is visible + * @returns {boolean} - True if the overflow button is visible, false otherwise + */ + function isOverflowButtonVisible() { + return $("#overflow-button").is(":visible"); + } + + /** + * Helper function to get the overflow button element + * @returns {jQuery} - The overflow button element + */ + function getOverflowButton() { + return $("#overflow-button"); + } + + /** + * Helper function to check if a tab is visible in the tab bar (not hidden by overflow) + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab is visible, false otherwise + */ + function isTabVisible(filePath) { + const $tab = getTab(filePath); + if (!$tab.length) { + return false; + } + + const $tabBar = $("#phoenix-tab-bar"); + const tabBarRect = $tabBar[0].getBoundingClientRect(); + const tabRect = $tab[0].getBoundingClientRect(); + + // A tab is considered visible if it is completely within the tab bar's visible area + // with a small margin of error (2px) + return tabRect.left >= tabBarRect.left && tabRect.right <= tabBarRect.right + 2; + } + + /** + * Helper function to get the overflow dropdown menu + * @returns {jQuery} - The overflow dropdown menu element + */ + function getOverflowDropdown() { + return $(".dropdown-overflow-menu"); + } + + /** + * Helper function to get the items in the overflow dropdown + * @returns {jQuery} - The overflow dropdown items + */ + function getOverflowDropdownItems() { + return $(".dropdown-overflow-menu .dropdown-tab-item"); + } + + /** + * Helper function to get a specific item in the overflow dropdown by file path + * @param {string} filePath - The path of the file to find + * @returns {jQuery} - The dropdown item element + */ + function getOverflowDropdownItem(filePath) { + return $(`.dropdown-overflow-menu .dropdown-tab-item[data-tab-path="${filePath}"]`); + } + describe("Visibility", function () { it("should show tab bar when the feature is enabled", async function () { // Enable the tab bar feature @@ -581,6 +642,269 @@ define(function (require, exports, module) { }); }); + describe("Overflow", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + }); + + it("should show overflow button when there are too many tabs to fit", async function () { + // Create several test files to ensure overflow + const testFiles = []; + for (let i = 0; i < 15; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() >= testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Verify the overflow button is visible + expect(isOverflowButtonVisible()).toBe(true); + + // Verify that some tabs are not visible + let visibleTabs = 0; + let hiddenTabs = 0; + for (const filePath of testFiles) { + if (isTabVisible(filePath)) { + visibleTabs++; + } else { + hiddenTabs++; + } + } + + // There should be at least one hidden tab + expect(hiddenTabs).toBeGreaterThan(0); + expect(visibleTabs + hiddenTabs).toBe(testFiles.length); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should display dropdown with hidden tabs when overflow button is clicked", async function () { + // Create several test files to ensure overflow + const testFiles = []; + for (let i = 0; i < 15; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() >= testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Get the list of hidden tabs + const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); + expect(hiddenFiles.length).toBeGreaterThan(0); + + // Click the overflow button + getOverflowButton().click(); + + // Wait for the dropdown to appear + await awaitsFor( + function () { + return getOverflowDropdown().length > 0; + }, + "Overflow dropdown to appear", + 1000 + ); + + // Verify the dropdown is visible + expect(getOverflowDropdown().length).toBeGreaterThan(0); + + // Verify the dropdown contains items for all hidden tabs + const dropdownItems = getOverflowDropdownItems(); + expect(dropdownItems.length).toBe(hiddenFiles.length); + + // Verify each hidden file has an item in the dropdown + for (const filePath of hiddenFiles) { + const item = getOverflowDropdownItem(filePath); + expect(item.length).toBe(1); + } + + // Clean up - close the dropdown by clicking elsewhere + $("body").click(); + + // Wait for the dropdown to disappear + await awaitsFor( + function () { + return getOverflowDropdown().length === 0; + }, + "Overflow dropdown to disappear", + 1000 + ); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should make tab visible and file active when clicking on item in overflow dropdown", async function () { + // Create several test files to ensure overflow + const testFiles = []; + for (let i = 0; i < 15; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() >= testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Get the list of hidden tabs + const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); + expect(hiddenFiles.length).toBeGreaterThan(0); + + // Select a hidden file to test + const testHiddenFile = hiddenFiles[0]; + + // Click the overflow button + getOverflowButton().click(); + + // Wait for the dropdown to appear + await awaitsFor( + function () { + return getOverflowDropdown().length > 0; + }, + "Overflow dropdown to appear", + 1000 + ); + + // Get the dropdown item for the test file + const dropdownItem = getOverflowDropdownItem(testHiddenFile); + expect(dropdownItem.length).toBe(1); + + // Click the dropdown item + dropdownItem.click(); + + // Wait for the file to become active + await awaitsFor( + function () { + return ( + isTabActive(testHiddenFile) && + MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile + ); + }, + "Hidden file to become active after dropdown item click", + 1000 + ); + + // Verify the file is active + expect(isTabActive(testHiddenFile)).toBe(true); + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testHiddenFile); + + // Verify the tab is now visible (scrolled into view) + await awaitsFor( + function () { + return isTabVisible(testHiddenFile); + }, + "Tab to become visible after dropdown item click", + 1000 + ); + + expect(isTabVisible(testHiddenFile)).toBe(true); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + }); + describe("Tab Items", function () { beforeEach(async function () { // Enable the tab bar feature From f12bb377e0fcb05aab23da7d0d0249b02542fdb6 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 21:02:35 +0530 Subject: [PATCH 10/26] feat: test for scroll to active tab --- test/spec/Extn-Tabbar-integ-test.js | 90 +++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index aae452c393..1c507d1337 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -903,6 +903,96 @@ define(function (require, exports, module) { await awaitsForDone(promise, `Close file ${filePath}`); } }); + + it("should scroll tab bar to make selected file visible when selecting from working set", async function () { + // Create several test files to ensure overflow + const testFiles = []; + for (let i = 0; i < 15; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() >= testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Get the list of hidden tabs + const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); + expect(hiddenFiles.length).toBeGreaterThan(0); + + // Select a hidden file to test + const testHiddenFile = hiddenFiles[0]; + + // Verify the tab is not visible initially + expect(isTabVisible(testHiddenFile)).toBe(false); + + // Select the file directly from the working set (not using the overflow dropdown) + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testHiddenFile }), + "Open hidden file from working set" + ); + + // Wait for the file to become active + await awaitsFor( + function () { + return ( + isTabActive(testHiddenFile) && + MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile + ); + }, + "Hidden file to become active after selection from working set", + 1000 + ); + + // Verify the file is active + expect(isTabActive(testHiddenFile)).toBe(true); + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testHiddenFile); + + // Verify the tab is now visible (scrolled into view) + await awaitsFor( + function () { + return isTabVisible(testHiddenFile); + }, + "Tab to become visible after selection from working set", + 1000 + ); + + expect(isTabVisible(testHiddenFile)).toBe(true); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); }); describe("Tab Items", function () { From f85459419d29a13b0ef307ffd216841ff04435aa Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 21:36:34 +0530 Subject: [PATCH 11/26] feat: write tests for context menu visibility --- test/spec/Extn-Tabbar-integ-test.js | 107 ++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 1c507d1337..ad5171d1fc 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -1309,5 +1309,112 @@ define(function (require, exports, module) { expect(fileClosePathArg).toBe(testFilePath); }); }); + + describe("Context Menu", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + }); + + /** + * Helper function to get the context menu element + * @returns {jQuery} - The context menu element + */ + function getContextMenu() { + return $(".tabbar-context-menu"); + } + + it("should open context menu when right-clicking on a tab", async function () { + // Get the tab element + const $tab = getTab(testFilePath); + expect($tab.length).toBe(1); + + // Simulate a right-click (contextmenu) event on the tab + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for the context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Verify the context menu is visible + expect(getContextMenu().length).toBe(1); + expect(getContextMenu().is(":visible")).toBe(true); + + // Clean up - close the context menu by clicking elsewhere + $("body").click(); + + // Wait for the context menu to disappear + await awaitsFor( + function () { + return getContextMenu().length === 0; + }, + "Context menu to disappear", + 1000 + ); + }); + + it("should close the tab when selecting 'Close Tab' from context menu", async function () { + // Get the full working set before closing the file + const initialWorkingSet = MainViewManager.getWorkingSet(MainViewManager.ACTIVE_PANE); + + // Verify the file is in the working set initially + expect(initialWorkingSet.some(file => file.fullPath === testFilePath)).toBe(true); + + // Get the file object (not just the path) + const fileObj = FileSystem.getFileForPath(testFilePath); + + // Close the file using FILE_CLOSE command with the full file object + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj }), + "Close file" + ); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + // Get the full working set after closing + const finalWorkingSet = MainViewManager.getWorkingSet(MainViewManager.ACTIVE_PANE); + + // Verify the file is removed from the working set + expect(finalWorkingSet.some(file => file.fullPath === testFilePath)).toBe(false); + }); + }); }); }); From 3af08d4a3360d2031361a1c24a0b670eb17c055b Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 21:52:11 +0530 Subject: [PATCH 12/26] fix: close tab context menu test --- test/spec/Extn-Tabbar-integ-test.js | 48 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index ad5171d1fc..93bead1cbc 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -24,7 +24,7 @@ define(function (require, exports, module) { const SpecRunnerUtils = require("spec/SpecRunnerUtils"); describe("integration:TabBar", function () { - let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, DocumentManager; + let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, DocumentManager, Strings; let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; beforeAll(async function () { @@ -38,6 +38,7 @@ define(function (require, exports, module) { CommandManager = testWindow.brackets.test.CommandManager; Commands = testWindow.brackets.test.Commands; DocumentManager = testWindow.brackets.test.DocumentManager; + Strings = testWindow.Strings; // Create test files testFilePath = SpecRunnerUtils.getTempDirectory() + "/tabbar-test.js"; @@ -1388,32 +1389,45 @@ define(function (require, exports, module) { }); it("should close the tab when selecting 'Close Tab' from context menu", async function () { - // Get the full working set before closing the file - const initialWorkingSet = MainViewManager.getWorkingSet(MainViewManager.ACTIVE_PANE); - - // Verify the file is in the working set initially - expect(initialWorkingSet.some(file => file.fullPath === testFilePath)).toBe(true); + // Get the tab element + const $tab = getTab(testFilePath); - // Get the file object (not just the path) - const fileObj = FileSystem.getFileForPath(testFilePath); + // Right-click on the tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); - // Close the file using FILE_CLOSE command with the full file object - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj }), - "Close file" + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 ); + // Find and click the "Close Tab" option + const $closeTabOption = getContextMenu().find('a.stylesheet-link').filter(function() { + return $(this).text().trim() === Strings.CLOSE_TAB; + }); + expect($closeTabOption.length).toBe(1); + $closeTabOption.click(); + // Cancel the save dialog if it appears testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE ); - // Get the full working set after closing - const finalWorkingSet = MainViewManager.getWorkingSet(MainViewManager.ACTIVE_PANE); - - // Verify the file is removed from the working set - expect(finalWorkingSet.some(file => file.fullPath === testFilePath)).toBe(false); + // Verify the tab is closed + await awaitsFor( + function () { + return !tabExists(testFilePath); + }, + "Tab to be closed", + 1000 + ); }); }); }); From e856c4590b95c5a55fa50ad540e2b8d6692daa06 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 22:01:21 +0530 Subject: [PATCH 13/26] feat: add tests for all close related option in context menu --- test/spec/Extn-Tabbar-integ-test.js | 352 +++++++++++++++++++++++++++- 1 file changed, 348 insertions(+), 4 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 93bead1cbc..dfbbbe0ee3 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -24,7 +24,15 @@ define(function (require, exports, module) { const SpecRunnerUtils = require("spec/SpecRunnerUtils"); describe("integration:TabBar", function () { - let testWindow, PreferencesManager, $, FileSystem, MainViewManager, CommandManager, Commands, DocumentManager, Strings; + let testWindow, + PreferencesManager, + $, + FileSystem, + MainViewManager, + CommandManager, + Commands, + DocumentManager, + Strings; let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; beforeAll(async function () { @@ -1408,9 +1416,11 @@ define(function (require, exports, module) { ); // Find and click the "Close Tab" option - const $closeTabOption = getContextMenu().find('a.stylesheet-link').filter(function() { - return $(this).text().trim() === Strings.CLOSE_TAB; - }); + const $closeTabOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_TAB; + }); expect($closeTabOption.length).toBe(1); $closeTabOption.click(); @@ -1429,6 +1439,340 @@ define(function (require, exports, module) { 1000 ); }); + + it("should close tabs to the right when selecting 'Close tabs to the right' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Get the first tab element + const $tab = getTab(testFilePath); + + // Right-click on the first tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close tabs to the right" option + const $closeTabsToRightOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_RIGHT; + }); + expect($closeTabsToRightOption.length).toBe(1); + $closeTabsToRightOption.click(); + + // Cancel any save dialogs that might appear + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + // Verify tabs to the right are closed + await awaitsFor( + function () { + return tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); + }, + "Tabs to the right to be closed", + 1000 + ); + + // Verify only the first tab remains + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(1); + }); + + it("should close tabs to the left when selecting 'Close tabs to the left' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Get the third tab element + const $tab = getTab(testFilePath3); + + // Right-click on the third tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close tabs to the left" option + const $closeTabsToLeftOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_LEFT; + }); + expect($closeTabsToLeftOption.length).toBe(1); + $closeTabsToLeftOption.click(); + + // Cancel any save dialogs that might appear + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + // Verify tabs to the left are closed + await awaitsFor( + function () { + return !tabExists(testFilePath) && !tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "Tabs to the left to be closed", + 1000 + ); + + // Verify only the third tab remains + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(1); + }); + + it("should close saved tabs when selecting 'Close saved tabs' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Make the second file dirty + const doc2 = DocumentManager.getOpenDocumentForPath(testFilePath2); + doc2.setText("// Modified content"); + + // Wait for the dirty indicator to appear + await awaitsFor( + function () { + return isTabDirty(testFilePath2); + }, + "Dirty indicator to appear", + 1000 + ); + + // Verify the second tab is dirty + expect(isTabDirty(testFilePath2)).toBe(true); + + // Get any tab element (we'll use the first) + const $tab = getTab(testFilePath); + + // Right-click on the tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close saved tabs" option + const $closeSavedTabsOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_SAVED_TABS; + }); + expect($closeSavedTabsOption.length).toBe(1); + $closeSavedTabsOption.click(); + + // Cancel any save dialogs that might appear + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + // Verify only the dirty tab remains + await awaitsFor( + function () { + return !tabExists(testFilePath) && tabExists(testFilePath2) && !tabExists(testFilePath3); + }, + "Saved tabs to be closed", + 1000 + ); + + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(1); + expect(isTabDirty(testFilePath2)).toBe(true); + + // Clean up - revert changes to the second file + doc2.setText("// Test file 2 for TabBar"); + await awaitsForDone( + CommandManager.execute(Commands.FILE_SAVE, { doc: doc2 }), + "Save file with original content" + ); + }); + + it("should close all tabs when selecting 'Close all tabs' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Get any tab element (we'll use the first) + const $tab = getTab(testFilePath); + + // Right-click on the tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close all tabs" option + const $closeAllTabsOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_ALL_TABS; + }); + expect($closeAllTabsOption.length).toBe(1); + $closeAllTabsOption.click(); + + // Cancel any save dialogs that might appear + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + // Verify all tabs are closed + await awaitsFor( + function () { + return !tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); + }, + "All tabs to be closed", + 1000 + ); + + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(0); + }); }); }); }); From aafd8f4b8cc2c53ea722b270de2da46fef404e31 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 6 Jul 2025 23:19:09 +0530 Subject: [PATCH 14/26] feat: add tests for split pane case --- test/spec/Extn-Tabbar-integ-test.js | 285 ++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index dfbbbe0ee3..f2d2a8ce3d 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -1774,5 +1774,290 @@ define(function (require, exports, module) { expect(getTabCount()).toBe(0); }); }); + + describe("Split Panes", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Set up a horizontal split view (two columns) + MainViewManager.setLayoutScheme(1, 2); + }); + + afterEach(async function () { + // Reset to single pane layout + MainViewManager.setLayoutScheme(1, 1); + }); + + /** + * Helper function to check if the tab bar for a specific pane is visible + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {boolean} - True if the tab bar is visible, false otherwise + */ + function isTabBarVisible(paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).is(":visible"); + } + + /** + * Helper function to get the tab count for a specific pane + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {number} - The number of tabs in the pane + */ + function getPaneTabCount(paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).find(".tab").length; + } + + /** + * Helper function to check if a tab for a specific file exists in a specific pane + * @param {string} filePath - The path of the file to check + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {boolean} - True if the tab exists in the pane, false otherwise + */ + function tabExistsInPane(filePath, paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).find(`.tab[data-path="${filePath}"]`).length > 0; + } + + it("should show tab bars in both panes when files are open in both", async function () { + // Open a file in the first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Open a different file in the second pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), + "Open file in second pane" + ); + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify both tab bars are visible + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Verify each pane has the correct tab + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(1); + expect(getPaneTabCount("second-pane")).toBe(1); + }); + + it("should hide tab bar in a pane with no files", async function () { + // Open a file in the first pane only + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Wait for the first tab bar to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane"); + }, + "First tab bar to appear", + 1000 + ); + + // Verify first tab bar is visible and second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Verify the first pane has the correct tab + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(1); + }); + + it("should update tab bars when moving files between panes", async function () { + // Open a file in the first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Wait for the first tab bar to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane"); + }, + "First tab bar to appear", + 1000 + ); + + // Verify first tab bar is visible and second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Move the file to the second pane + const fileObj = FileSystem.getFileForPath(testFilePath); + MainViewManager.addToWorkingSet("second-pane", fileObj); + + // Remove from first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: "first-pane" }), + "Close file in first pane" + ); + + // Wait for the tab to appear in the second pane and disappear from the first + await awaitsFor( + function () { + return ( + !tabExistsInPane(testFilePath, "first-pane") && tabExistsInPane(testFilePath, "second-pane") + ); + }, + "Tab to move to second pane", + 1000 + ); + + // Verify the tab bars visibility has updated + expect(isTabBarVisible("first-pane")).toBe(false); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Verify the tab is now in the second pane + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(false); + expect(tabExistsInPane(testFilePath, "second-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(0); + expect(getPaneTabCount("second-pane")).toBe(1); + }); + + it("should hide tab bar when closing all files in a pane", async function () { + // Open files in both panes + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), + "Open file in second pane" + ); + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify both tab bars are visible + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Close the file in the second pane + const fileToClose = FileSystem.getFileForPath(testFilePath2); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { + file: fileToClose, + paneId: "second-pane" + }); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + await awaitsForDone(promise, "Close file in second pane"); + + // Wait for the second tab bar to disappear + await awaitsFor( + function () { + return !isTabBarVisible("second-pane"); + }, + "Second tab bar to disappear", + 1000 + ); + + // Verify first tab bar is still visible but second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Verify the tabs are in the correct panes + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(false); + expect(getPaneTabCount("first-pane")).toBe(1); + expect(getPaneTabCount("second-pane")).toBe(0); + }); + + it("should work correctly with vertical split layout", async function () { + // Change to vertical split layout (2 rows, 1 column) + MainViewManager.setLayoutScheme(2, 1); + + // Open a file in the first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Open a different file in the second pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), + "Open file in second pane" + ); + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify both tab bars are visible + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Verify each pane has the correct tab + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(1); + expect(getPaneTabCount("second-pane")).toBe(1); + + // Close the file in the second pane + const fileToClose = FileSystem.getFileForPath(testFilePath2); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { + file: fileToClose, + paneId: "second-pane" + }); + + // Cancel the save dialog if it appears + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + + await awaitsForDone(promise, "Close file in second pane"); + + // Wait for the second tab bar to disappear + await awaitsFor( + function () { + return !isTabBarVisible("second-pane"); + }, + "Second tab bar to disappear", + 1000 + ); + + // Verify first tab bar is still visible but second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Reset to horizontal split for other tests + MainViewManager.setLayoutScheme(1, 2); + }); + }); }); }); From 8b63c62fc1e1876a58dfbc750baec4705c22332a Mon Sep 17 00:00:00 2001 From: Pluto Date: Tue, 8 Jul 2025 20:46:19 +0530 Subject: [PATCH 15/26] feat: tests for number of tabs preference setting --- test/spec/Extn-Tabbar-integ-test.js | 248 ++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index f2d2a8ce3d..8e44452dd0 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -1775,6 +1775,254 @@ define(function (require, exports, module) { }); }); + describe("Number of Tabs Preference", function () { + beforeEach(async function () { + // Enable the tab bar feature with default settings + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + }); + + afterEach(async function () { + // Reset preferences to default + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + }); + + it("should show all tabs when numberOfTabs is set to -1", async function () { + // Create several test files + const testFiles = []; + for (let i = 0; i < 10; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() >= testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Verify all tabs are shown + expect(getTabCount()).toBe(testFiles.length); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should limit the number of tabs shown when numberOfTabs is set to a positive value", async function () { + // Set the preference to show only 5 tabs + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 5 }); + + // Create several test files + const testFiles = []; + for (let i = 0; i < 10; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for tabs to appear + await awaitsFor( + function () { + return getTabCount() > 0; + }, + "Tabs to appear", + 1000 + ); + + // Verify only 5 tabs are shown + expect(getTabCount()).toBe(5); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should hide the tab bar when numberOfTabs is set to 0", async function () { + // First open some files with the default setting + const testFiles = []; + for (let i = 0; i < 3; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for tabs to appear + await awaitsFor( + function () { + return getTabCount() > 0; + }, + "Tabs to appear", + 1000 + ); + + // Verify tab bar is visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(true); + + // Now set numberOfTabs to 0 + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 0 }); + + // Wait for tab bar to disappear + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to disappear", + 1000 + ); + + // Verify tab bar is hidden + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should apply numberOfTabs preference to both panes", async function () { + // Set up split pane layout + MainViewManager.setLayoutScheme(1, 2); + + // Set the preference to show only 3 tabs + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 3 }); + + // Create test files for first pane + const firstPaneFiles = []; + for (let i = 0; i < 5; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-${i}.js`; + firstPaneFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// First pane file ${i}`, FileSystem)); + } + + // Create test files for second pane + const secondPaneFiles = []; + for (let i = 0; i < 5; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/second-pane-${i}.js`; + secondPaneFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Second pane file ${i}`, FileSystem)); + } + + // Open files in first pane + for (const filePath of firstPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), + `Open file ${filePath} in first pane` + ); + } + + // Open files in second pane + for (const filePath of secondPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "second-pane" }), + `Open file ${filePath} in second pane` + ); + } + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible") && $("#phoenix-tab-bar-2").is(":visible"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify each pane shows only 3 tabs + expect($("#phoenix-tab-bar").find(".tab").length).toBe(3); + expect($("#phoenix-tab-bar-2").find(".tab").length).toBe(3); + + // Change preference to show all tabs + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return ( + $("#phoenix-tab-bar").find(".tab").length === 5 && + $("#phoenix-tab-bar-2").find(".tab").length === 5 + ); + }, + "All tabs to appear in both panes", + 1000 + ); + + // Verify all tabs are shown in both panes + expect($("#phoenix-tab-bar").find(".tab").length).toBe(5); + expect($("#phoenix-tab-bar-2").find(".tab").length).toBe(5); + + // Change preference to hide tab bars + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 0 }); + + // Wait for both tab bars to disappear + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible") && !$("#phoenix-tab-bar-2").is(":visible"); + }, + "Both tab bars to disappear", + 1000 + ); + + // Verify both tab bars are hidden + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + expect($("#phoenix-tab-bar-2").is(":visible")).toBe(false); + + // Clean up - close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + }); + }); + describe("Split Panes", function () { beforeEach(async function () { // Enable the tab bar feature From 3fee41daffc4e48e4602673a2ad882964b3024b2 Mon Sep 17 00:00:00 2001 From: Pluto Date: Tue, 8 Jul 2025 22:53:52 +0530 Subject: [PATCH 16/26] feat: integ tests for tab bar scrolling --- test/spec/Extn-Tabbar-integ-test.js | 268 ++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 8e44452dd0..c473280a9d 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -2307,5 +2307,273 @@ define(function (require, exports, module) { MainViewManager.setLayoutScheme(1, 2); }); }); + + describe("Tab Bar Scrolling", function () { + let longTestFilePaths = []; + + beforeEach(async function () { + // Create multiple test files to ensure scrolling is needed + longTestFilePaths = []; + for (let i = 1; i <= 15; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/scroll-test-file-${i}.js`; + longTestFilePaths.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// Test file ${i} for scrolling`, FileSystem) + ); + } + + // Open all files to create many tabs + for (let filePath of longTestFilePaths) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open ${filePath}` + ); + } + + // Wait for tabs to be rendered + await awaitsFor( + function () { + return getTabCount() >= 15; + }, + "All tabs to be created", + 3000 + ); + }); + + afterEach(async function () { + // Close all test files + for (let filePath of longTestFilePaths) { + const fileObj = FileSystem.getFileForPath(filePath); + try { + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj }), + `Close ${filePath}` + ); + } catch (e) { + // Ignore errors if file is already closed + } + } + }); + + it("should scroll tab bar horizontally when mouse wheel is scrolled", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Get initial scroll position + const initialScrollLeft = $tabBar.scrollLeft(); + + // Create a wheel event for scrolling down (should scroll right) + const wheelEventDown = $.Event("wheel"); + wheelEventDown.originalEvent = { deltaY: 100 }; // Positive deltaY = scroll down/right + + // Trigger the wheel event + $tabBar.trigger(wheelEventDown); + + // Check that scroll position has changed to the right + const scrollAfterDown = $tabBar.scrollLeft(); + expect(scrollAfterDown).toBeGreaterThan(initialScrollLeft); + // Verify the scroll amount is proportional to deltaY (implementation multiplies by 2.5) + expect(scrollAfterDown - initialScrollLeft).toBeCloseTo(100 * 2.5, 0); + + // Create a wheel event for scrolling up (should scroll left) + const wheelEventUp = $.Event("wheel"); + wheelEventUp.originalEvent = { deltaY: -100 }; // Negative deltaY = scroll up/left + + // Trigger the wheel event + $tabBar.trigger(wheelEventUp); + + // Check that scroll position has moved left from the previous position + const scrollAfterUp = $tabBar.scrollLeft(); + expect(scrollAfterUp).toBeLessThan(scrollAfterDown); + // Verify the scroll amount is proportional to deltaY + expect(scrollAfterDown - scrollAfterUp).toBeCloseTo(100 * 2.5, 0); + }); + + it("should scroll tab bar with trackpad scrolling", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Get initial scroll position + const initialScrollLeft = $tabBar.scrollLeft(); + + // Create a wheel event simulating trackpad scrolling (smaller deltaY values) + const trackpadEvent = $.Event("wheel"); + trackpadEvent.originalEvent = { deltaY: 25 }; // Smaller value typical of trackpad + + // Trigger the trackpad scrolling event multiple times + for (let i = 0; i < 4; i++) { + $tabBar.trigger(trackpadEvent); + } + + // Check that scroll position has changed + const scrollAfterTrackpad = $tabBar.scrollLeft(); + expect(scrollAfterTrackpad).toBeGreaterThan(initialScrollLeft); + // Verify the total scroll amount after multiple small scrolls + // 4 scrolls of 25 * 2.5 = 250 pixels total + expect(scrollAfterTrackpad - initialScrollLeft).toBeCloseTo(4 * 25 * 2.5, 0); + }); + + it("should scroll second pane tab bar when it exists", async function () { + // Create the second pane first + MainViewManager.setLayoutScheme(1, 2); + + // Wait for the layout to be created + await awaitsFor( + function () { + return MainViewManager.getPaneIdList().length === 2; + }, + "Second pane to be created", + 1000 + ); + + // Open a file in the second pane to create the second tab bar + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { + fullPath: longTestFilePaths[0], + paneId: "second-pane" + }), + "Open file in second pane" + ); + + // Wait for second tab bar to appear + await awaitsFor( + function () { + return $("#phoenix-tab-bar-2").length > 0; + }, + "Second tab bar to appear", + 1000 + ); + + const $tabBar2 = $("#phoenix-tab-bar-2"); + expect($tabBar2.length).toBe(1); + + // Open multiple files in second pane to enable scrolling + for (let i = 1; i < 8; i++) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { + fullPath: longTestFilePaths[i], + paneId: "second-pane" + }), + `Open file ${i} in second pane` + ); + } + + // Wait for tabs to be rendered in second pane + await awaitsFor( + function () { + return $tabBar2.find(".tab").length >= 8; + }, + "Tabs to be created in second pane", + 2000 + ); + + // Get initial scroll position of second tab bar + const initialScrollLeft = $tabBar2.scrollLeft(); + + // Create a wheel event for scrolling + const wheelEvent = $.Event("wheel"); + wheelEvent.originalEvent = { deltaY: 150 }; + + // Trigger the wheel event on second tab bar + $tabBar2.trigger(wheelEvent); + + // Check that scroll position has changed + const scrollAfterWheel = $tabBar2.scrollLeft(); + expect(scrollAfterWheel).toBeGreaterThan(initialScrollLeft); + // Verify the scroll amount is proportional to deltaY + expect(scrollAfterWheel - initialScrollLeft).toBeCloseTo(150 * 2.5, 0); + + // Reset layout scheme back to single pane + MainViewManager.setLayoutScheme(1, 1); + }); + + it("should calculate correct scroll amount based on deltaY", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Ensure the tab bar is scrollable by checking if scrollWidth > clientWidth + if ($tabBar[0].scrollWidth <= $tabBar[0].clientWidth) { + // Skip test if tab bar is not scrollable + return; + } + + // Set initial scroll position to middle to allow scrolling in both directions + const maxScroll = $tabBar[0].scrollWidth - $tabBar[0].clientWidth; + const midScroll = Math.floor(maxScroll / 2); + $tabBar.scrollLeft(midScroll); + + // Test positive deltaY (scroll right) + const initialScrollLeft = $tabBar.scrollLeft(); + const wheelEventRight = $.Event("wheel"); + wheelEventRight.originalEvent = { deltaY: 40 }; + $tabBar.trigger(wheelEventRight); + + const scrollAfterRight = $tabBar.scrollLeft(); + expect(scrollAfterRight).toBeGreaterThan(initialScrollLeft); + expect(scrollAfterRight - initialScrollLeft).toBeCloseTo(40 * 2.5, 0); + + // Reset and test negative deltaY (scroll left) + $tabBar.scrollLeft(midScroll); + const wheelEventLeft = $.Event("wheel"); + wheelEventLeft.originalEvent = { deltaY: -40 }; + $tabBar.trigger(wheelEventLeft); + + const scrollAfterLeft = $tabBar.scrollLeft(); + expect(scrollAfterLeft).toBeLessThan(midScroll); + expect(midScroll - scrollAfterLeft).toBeCloseTo(40 * 2.5, 0); + }); + + it("should not scroll beyond the scrollable bounds", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Get the maximum scrollable width + const maxScrollLeft = $tabBar[0].scrollWidth - $tabBar[0].clientWidth; + + // Scroll far to the right + $tabBar.scrollLeft(maxScrollLeft + 1000); // Try to scroll beyond max + + // Create a wheel event to scroll further right + const wheelEventRight = $.Event("wheel"); + wheelEventRight.originalEvent = { deltaY: 500 }; // Large scroll right + $tabBar.trigger(wheelEventRight); + + // Should not exceed maximum scroll + expect($tabBar.scrollLeft()).toBeLessThanOrEqual(maxScrollLeft); + + // Scroll far to the left + $tabBar.scrollLeft(-1000); // Try to scroll beyond minimum + + // Create a wheel event to scroll further left + const wheelEventLeft = $.Event("wheel"); + wheelEventLeft.originalEvent = { deltaY: -500 }; // Large scroll left + $tabBar.trigger(wheelEventLeft); + + // Should not go below 0 + expect($tabBar.scrollLeft()).toBeGreaterThanOrEqual(0); + }); + + it("should handle rapid consecutive scroll events", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + const initialScrollLeft = $tabBar.scrollLeft(); + + // Trigger multiple rapid scroll events + for (let i = 0; i < 10; i++) { + const wheelEvent = $.Event("wheel"); + wheelEvent.originalEvent = { deltaY: 50 }; + $tabBar.trigger(wheelEvent); + } + + // Should have scrolled significantly + const finalScrollLeft = $tabBar.scrollLeft(); + expect(finalScrollLeft).toBeGreaterThan(initialScrollLeft + 100); + + // Verify the total scroll amount after multiple events + // 10 scrolls of 50 * 2.5 = 1250 pixels total + expect(finalScrollLeft - initialScrollLeft).toBeCloseTo(10 * 50 * 2.5, 0); + }); + }); }); }); From f8708eb2a0418d538773e5f581768a7079690a92 Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 00:07:10 +0530 Subject: [PATCH 17/26] feat: tests for drag-drop feature for tab bar --- src/extensionsIntegrated/TabBar/drag-drop.js | 4 +- test/spec/Extn-Tabbar-integ-test.js | 580 ++++++++++++++++++- 2 files changed, 552 insertions(+), 32 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/drag-drop.js b/src/extensionsIntegrated/TabBar/drag-drop.js index 0a4dadf2ef..578ce516a9 100644 --- a/src/extensionsIntegrated/TabBar/drag-drop.js +++ b/src/extensionsIntegrated/TabBar/drag-drop.js @@ -784,7 +784,9 @@ define(function (require, exports, module) { $(this).addClass("empty-pane-drop-target"); // set the drop effect - e.originalEvent.dataTransfer.dropEffect = "move"; + if (e.originalEvent && e.originalEvent.dataTransfer) { + e.originalEvent.dataTransfer.dropEffect = "move"; + } } }); diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index c473280a9d..70a6d5fb79 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -35,6 +35,37 @@ define(function (require, exports, module) { Strings; let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; + /** + * Helper function to check if the tab bar for a specific pane is visible + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {boolean} - True if the tab bar is visible, false otherwise + */ + function isTabBarVisible(paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).is(":visible"); + } + + /** + * Helper function to get the tab count for a specific pane + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {number} - The number of tabs in the pane + */ + function getPaneTabCount(paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).find(".tab").length; + } + + /** + * Helper function to check if a tab for a specific file exists in a specific pane + * @param {string} filePath - The path of the file to check + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {boolean} - True if the tab exists in the pane, false otherwise + */ + function tabExistsInPane(filePath, paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).find(`.tab[data-path="${filePath}"]`).length > 0; + } + beforeAll(async function () { // Create the test window testWindow = await SpecRunnerUtils.createTestWindowAndRun(); @@ -301,6 +332,524 @@ define(function (require, exports, module) { }); }); + describe("Drag and Drop", function () { + beforeEach(async function () { + // Close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + + // Wait for cleanup to complete + await awaits(100); + }); + + it("should allow dragging and dropping a tab to the beginning of the tab bar", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Create and open multiple test files to work with + const testFiles = []; + for (let i = 0; i < 3; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/drag-drop-test-${i}.js`; + testFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// Drag drop test file ${i}`, FileSystem) + ); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() === testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Verify initial tab order + const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(initialWorkingSet.length).toBe(testFiles.length); + expect(initialWorkingSet[0].fullPath).toBe(testFiles[0]); + expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); + expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); + + // Get the source and target tabs + const sourceTab = getTab(testFiles[0]); + const targetTab = getTab(testFiles[2]); + + // Simulate drag start on the first tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the last tab + const dragEnterEvent = $.Event("dragenter"); + targetTab.trigger(dragEnterEvent); + + // Simulate drag over on the last tab + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + }, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {} + }); + targetTab.trigger(dragOverEvent); + + // Simulate drop on the last tab + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {}, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + targetTab.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + + // Wait for the working set to update + await awaitsFor( + function () { + const currentWorkingSet = MainViewManager.getWorkingSet("first-pane"); + // Check if the first file has moved to before the last file + return ( + currentWorkingSet.length === testFiles.length && + currentWorkingSet[1].fullPath === testFiles[0] + ); + }, + "Working set to update after drag and drop", + 1000 + ); + + // Verify the new tab order + const finalWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(finalWorkingSet.length).toBe(testFiles.length); + expect(finalWorkingSet[0].fullPath).toBe(testFiles[1]); + expect(finalWorkingSet[1].fullPath).toBe(testFiles[0]); + expect(finalWorkingSet[2].fullPath).toBe(testFiles[2]); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should allow dragging and dropping a tab in between other tabs", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Create and open multiple test files to work with + const testFiles = []; + for (let i = 0; i < 3; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/drag-between-test-${i}.js`; + testFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// Drag between test file ${i}`, FileSystem) + ); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() === testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Verify initial tab order + const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(initialWorkingSet.length).toBe(testFiles.length); + expect(initialWorkingSet[0].fullPath).toBe(testFiles[0]); + expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); + expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); + + // Get the source and target tabs - we'll drag the last tab to between the first and second tabs + const sourceTab = getTab(testFiles[2]); // Last tab + const targetTab = getTab(testFiles[1]); // Middle tab + + // Simulate drag start on the last tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the middle tab + const dragEnterEvent = $.Event("dragenter"); + targetTab.trigger(dragEnterEvent); + + // Simulate drag over on the middle tab - position near the left edge to drop before it + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + }, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {} + }); + targetTab.trigger(dragOverEvent); + + // Simulate drop on the middle tab + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {}, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + targetTab.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + + // Wait for the working set to update + await awaitsFor( + function () { + const currentWorkingSet = MainViewManager.getWorkingSet("first-pane"); + // Check if the last file has moved to between the first and second files + return ( + currentWorkingSet.length === testFiles.length && + currentWorkingSet[1].fullPath === testFiles[2] + ); + }, + "Working set to update after drag and drop", + 1000 + ); + + // Verify the new tab order + const finalWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(finalWorkingSet.length).toBe(testFiles.length); + expect(finalWorkingSet[0].fullPath).toBe(testFiles[0]); + expect(finalWorkingSet[1].fullPath).toBe(testFiles[2]); // Last tab should now be in the middle + expect(finalWorkingSet[2].fullPath).toBe(testFiles[1]); // Middle tab should now be last + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should allow dragging a tab from one pane to another non-empty pane", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Set up a horizontal split view (two columns) + MainViewManager.setLayoutScheme(1, 2); + + // Create test files for both panes + const firstPaneFiles = []; + for (let i = 0; i < 2; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-test-${i}.js`; + firstPaneFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// First pane test file ${i}`, FileSystem) + ); + } + + const secondPaneFiles = []; + for (let i = 0; i < 2; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/second-pane-test-${i}.js`; + secondPaneFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// Second pane test file ${i}`, FileSystem) + ); + } + + // Open files in the first pane + for (const filePath of firstPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), + `Open file ${filePath} in first pane` + ); + } + + // Open files in the second pane + for (const filePath of secondPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "second-pane" }), + `Open file ${filePath} in second pane` + ); + } + + // Wait for all tabs to appear in both panes + await awaitsFor( + function () { + return ( + getPaneTabCount("first-pane") === firstPaneFiles.length && + getPaneTabCount("second-pane") === secondPaneFiles.length + ); + }, + "All tabs to appear in both panes", + 1000 + ); + + // Verify initial tab counts + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); + expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length); + + // Get the source tab from the first pane and target tab from the second pane + const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); + const targetTab = $(`.tab[data-path="${secondPaneFiles[0]}"]`); + + // Simulate drag start on the source tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the target tab + const dragEnterEvent = $.Event("dragenter"); + targetTab.trigger(dragEnterEvent); + + // Simulate drag over on the target tab + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + }, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {} + }); + targetTab.trigger(dragOverEvent); + + // Simulate drop on the target tab + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {}, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + targetTab.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + + // Wait for the tab to move to the second pane + await awaitsFor( + function () { + return ( + !tabExistsInPane(firstPaneFiles[0], "first-pane") && + tabExistsInPane(firstPaneFiles[0], "second-pane") + ); + }, + "Tab to move from first pane to second pane", + 1000 + ); + + // Verify the tab counts after the drag and drop + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); + expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length + 1); + + // Verify the tab is now in the second pane + expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); + expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); + + // Clean up - close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + }); + + it("should allow dragging a tab to an empty pane", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Set up a horizontal split view (two columns) + MainViewManager.setLayoutScheme(1, 2); + + // Wait for layout to settle and ensure second pane is empty + await awaits(100); + + // Force close any files that might be open in the second pane + const secondPaneWorkingSet = MainViewManager.getWorkingSet("second-pane"); + for (const file of secondPaneWorkingSet) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: file, paneId: "second-pane" }), + `Force close file ${file.fullPath} in second pane` + ); + } + + // Create test files for the first pane + const firstPaneFiles = []; + for (let i = 0; i < 2; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-empty-test-${i}.js`; + firstPaneFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// First pane empty test file ${i}`, FileSystem) + ); + } + + // Open files in the first pane only + for (const filePath of firstPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), + `Open file ${filePath} in first pane` + ); + } + + // Wait for all tabs to appear in the first pane + await awaitsFor( + function () { + return getPaneTabCount("first-pane") === firstPaneFiles.length; + }, + "All tabs to appear in first pane", + 1000 + ); + + // Ensure second pane is empty before proceeding + await awaitsFor( + function () { + return getPaneTabCount("second-pane") === 0 && !isTabBarVisible("second-pane"); + }, + "Second pane to be empty", + 1000 + ); + + // Verify initial tab counts + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); + expect(getPaneTabCount("second-pane")).toBe(0); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Get the source tab from the first pane + const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); + + // Get the empty pane content area as the drop target + const emptyPaneTarget = $("#second-pane .pane-content"); + + // Simulate drag start on the source tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the empty pane + const dragEnterEvent = $.Event("dragenter"); + emptyPaneTarget.trigger(dragEnterEvent); + + // Simulate drag over on the empty pane + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + } + }, + preventDefault: function () {} + }); + emptyPaneTarget.trigger(dragOverEvent); + + // Simulate drop on the empty pane + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {} + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + emptyPaneTarget.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + + // Wait for the tab to move to the second pane + await awaitsFor( + function () { + return ( + !tabExistsInPane(firstPaneFiles[0], "first-pane") && + tabExistsInPane(firstPaneFiles[0], "second-pane") && + isTabBarVisible("second-pane") + ); + }, + "Tab to move from first pane to second pane and tab bar to appear", + 1000 + ); + + // Verify the tab counts after the drag and drop + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); + expect(getPaneTabCount("second-pane")).toBe(1); + + // Verify the tab is now in the second pane + expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); + expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); + + // Clean up - close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + }); + }); + describe("Working Set", function () { beforeEach(async function () { // Enable the tab bar feature @@ -2040,37 +2589,6 @@ define(function (require, exports, module) { MainViewManager.setLayoutScheme(1, 1); }); - /** - * Helper function to check if the tab bar for a specific pane is visible - * @param {string} paneId - The pane ID ("first-pane" or "second-pane") - * @returns {boolean} - True if the tab bar is visible, false otherwise - */ - function isTabBarVisible(paneId) { - const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; - return $(tabBarId).is(":visible"); - } - - /** - * Helper function to get the tab count for a specific pane - * @param {string} paneId - The pane ID ("first-pane" or "second-pane") - * @returns {number} - The number of tabs in the pane - */ - function getPaneTabCount(paneId) { - const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; - return $(tabBarId).find(".tab").length; - } - - /** - * Helper function to check if a tab for a specific file exists in a specific pane - * @param {string} filePath - The path of the file to check - * @param {string} paneId - The pane ID ("first-pane" or "second-pane") - * @returns {boolean} - True if the tab exists in the pane, false otherwise - */ - function tabExistsInPane(filePath, paneId) { - const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; - return $(tabBarId).find(`.tab[data-path="${filePath}"]`).length > 0; - } - it("should show tab bars in both panes when files are open in both", async function () { // Open a file in the first pane await awaitsForDone( From 13464c25f5f34f27683c048144b39ac8ec39192d Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 00:49:03 +0530 Subject: [PATCH 18/26] feat: tests for configure working set button's tab bar and working set show/hide --- test/spec/Extn-Tabbar-integ-test.js | 232 +++++++++++++++++++++++++--- 1 file changed, 210 insertions(+), 22 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 70a6d5fb79..c611582504 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -330,6 +330,216 @@ define(function (require, exports, module) { // Verify the tab bar is not visible expect($("#phoenix-tab-bar").is(":visible")).toBe(false); }); + + it("should show working set when the option is enabled", async function () { + // Enable the working set feature + PreferencesManager.set("showWorkingSet", true); + + // Wait for the working set to become visible + await awaitsFor( + function () { + return !$("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become visible", + 1000 + ); + + // Verify the working set is visible + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); + }); + + it("should hide working set when the option is disabled", async function () { + // Disable the working set feature + PreferencesManager.set("showWorkingSet", false); + + // Wait for the working set to become hidden + await awaitsFor( + function () { + return $("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become hidden", + 1000 + ); + + // Verify the working set is not visible + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); + }); + }); + + describe("Configure Working Set Button", function () { + it("should have a working set configuration button in the sidebar", function () { + // Verify the button exists + const $configButton = $(".working-set-splitview-btn"); + expect($configButton.length).toBe(1); + }); + + it("should open a menu with 'Show working set' and 'Show file tab bar' options when clicked", async function () { + // Click the configure working set button + const $configButton = $(".working-set-splitview-btn"); + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Verify the menu contains the expected options + const $menu = $(".dropdown-menu:visible"); + const showWorkingSetItem = $menu.find("li a[id$='cmd.toggleShowWorkingSet']"); + const showFileTabsItem = $menu.find("li a[id$='cmd.toggleShowFileTabs']"); + + expect(showWorkingSetItem.length).toBe(1); + expect(showFileTabsItem.length).toBe(1); + + // Clean up - close the menu + $("body").click(); + }); + + it("should toggle working set visibility when 'Show working set' option is clicked", async function () { + // First, ensure working set is visible + PreferencesManager.set("showWorkingSet", true); + await awaitsFor( + function () { + return !$("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become visible", + 1000 + ); + + // Click the configure working set button + const $configButton = $(".working-set-splitview-btn"); + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show working set" option + const $menu = $(".dropdown-menu:visible"); + const showWorkingSetItem = $menu.find("li a[id$='cmd.toggleShowWorkingSet']"); + showWorkingSetItem.click(); + + // Wait for the working set to become hidden + await awaitsFor( + function () { + return $("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become hidden", + 1000 + ); + + // Verify the working set is hidden + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); + + // Click the configure working set button again + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show working set" option again + const $menu2 = $(".dropdown-menu:visible"); + const showWorkingSetItem2 = $menu2.find("li a[id$='cmd.toggleShowWorkingSet']"); + showWorkingSetItem2.click(); + + // Wait for the working set to become visible + await awaitsFor( + function () { + return !$("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become visible", + 1000 + ); + + // Verify the working set is visible + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); + }); + + it("should toggle tab bar visibility when 'Show file tab bar' option is clicked", async function () { + // First, ensure tab bar is visible + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become visible", + 1000 + ); + + // Click the configure working set button + const $configButton = $(".working-set-splitview-btn"); + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show file tab bar" option + const $menu = $(".dropdown-menu:visible"); + const showFileTabsItem = $menu.find("li a[id$='cmd.toggleShowFileTabs']"); + showFileTabsItem.click(); + + // Wait for the tab bar to become hidden + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become hidden", + 1000 + ); + + // Verify the tab bar is hidden + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + + // Click the configure working set button again + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show file tab bar" option again + const $menu2 = $(".dropdown-menu:visible"); + const showFileTabsItem2 = $menu2.find("li a[id$='cmd.toggleShowFileTabs']"); + showFileTabsItem2.click(); + + // Wait for the tab bar to become visible + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become visible", + 1000 + ); + + // Verify the tab bar is visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(true); + }); }); describe("Drag and Drop", function () { @@ -3070,28 +3280,6 @@ define(function (require, exports, module) { // Should not go below 0 expect($tabBar.scrollLeft()).toBeGreaterThanOrEqual(0); }); - - it("should handle rapid consecutive scroll events", function () { - const $tabBar = $("#phoenix-tab-bar"); - expect($tabBar.length).toBe(1); - - const initialScrollLeft = $tabBar.scrollLeft(); - - // Trigger multiple rapid scroll events - for (let i = 0; i < 10; i++) { - const wheelEvent = $.Event("wheel"); - wheelEvent.originalEvent = { deltaY: 50 }; - $tabBar.trigger(wheelEvent); - } - - // Should have scrolled significantly - const finalScrollLeft = $tabBar.scrollLeft(); - expect(finalScrollLeft).toBeGreaterThan(initialScrollLeft + 100); - - // Verify the total scroll amount after multiple events - // 10 scrolls of 50 * 2.5 = 1250 pixels total - expect(finalScrollLeft - initialScrollLeft).toBeCloseTo(10 * 50 * 2.5, 0); - }); }); }); }); From e50010f8058839277defaa7924f0f09e94d5a456 Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 11:11:39 +0530 Subject: [PATCH 19/26] refactor: improve code readability --- test/spec/Extn-Tabbar-integ-test.js | 815 +++++++++------------------- 1 file changed, 263 insertions(+), 552 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index c611582504..7466099782 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -35,6 +35,175 @@ define(function (require, exports, module) { Strings; let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; + /** + * Helper function to create multiple test files + * @param {number} count - Number of files to create + * @param {string} prefix - Prefix for the file names + * @param {string} content - Content template for the files (will be appended with file index) + * @returns {Promise} - Array of file paths + */ + async function createTestFiles(count, prefix, content) { + const testFiles = []; + for (let i = 0; i < count; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/${prefix}-${i}.js`; + testFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile( + filePath, + content ? `${content} ${i}` : `// ${prefix} test file ${i}`, + FileSystem + ) + ); + } + return testFiles; + } + + /** + * Helper function to open multiple files + * @param {string[]} filePaths - Array of file paths to open + * @param {string} [paneId] - Optional pane ID to open the files in + * @returns {Promise} + */ + async function openTestFiles(filePaths, paneId) { + for (const filePath of filePaths) { + const options = { fullPath: filePath }; + if (paneId) { + options.paneId = paneId; + } + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, options), + `Open file ${filePath}${paneId ? ` in ${paneId}` : ""}` + ); + } + } + + /** + * Helper function to wait for tabs to appear + * @param {string[]} filePaths - Array of file paths to wait for + * @param {string} [paneId] - Optional pane ID to check for tabs + * @returns {Promise} + */ + async function waitForTabs(filePaths, paneId) { + if (paneId) { + // Wait for tabs to appear in the specified pane + await awaitsFor( + function () { + return getPaneTabCount(paneId) >= filePaths.length; + }, + `All tabs to appear in ${paneId}`, + 1000 + ); + } else if (filePaths.length === 1) { + // Wait for a single tab to appear + await awaitsFor( + function () { + return tabExists(filePaths[0]); + }, + `Tab for ${filePaths[0]} to appear`, + 1000 + ); + } else { + // Wait for multiple tabs to appear + await awaitsFor( + function () { + return getTabCount() >= filePaths.length && filePaths.every((path) => tabExists(path)); + }, + "All tabs to appear", + 1000 + ); + } + } + + /** + * Helper function to cancel the save dialog + * @returns {void} + */ + function cancelSaveDialog() { + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + } + + /** + * Helper function to close files + * @param {string[]} filePaths - Array of file paths to close + * @param {string} [paneId] - Optional pane ID to close the files from + * @returns {Promise} + */ + async function closeTestFiles(filePaths, paneId) { + for (const filePath of filePaths) { + const fileToClose = FileSystem.getFileForPath(filePath); + const options = { file: fileToClose }; + if (paneId) { + options.paneId = paneId; + } + const promise = CommandManager.execute(Commands.FILE_CLOSE, options); + cancelSaveDialog(); + await awaitsForDone(promise, `Close file ${filePath}`); + } + } + + /** + * Helper function to simulate drag and drop between tabs + * @param {string} sourceFilePath - Path of the source file to drag + * @param {string} targetFilePath - Path of the target file to drop onto + * @param {boolean} [dropBefore=true] - Whether to drop before the target (true) or after (false) + * @returns {Promise} + */ + async function simulateTabDragAndDrop(sourceFilePath, targetFilePath, dropBefore = true) { + // Get the source and target tabs + const sourceTab = getTab(sourceFilePath); + const targetTab = getTab(targetFilePath); + + // Simulate drag start on the source tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the target tab + const dragEnterEvent = $.Event("dragenter"); + targetTab.trigger(dragEnterEvent); + + // Simulate drag over on the target tab + const targetRect = targetTab[0].getBoundingClientRect(); + const dropX = dropBefore + ? targetRect.left + 5 // Position near the left edge to drop before + : targetRect.right - 5; // Position near the right edge to drop after + + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + }, + clientX: dropX + }, + preventDefault: function () {} + }); + targetTab.trigger(dragOverEvent); + + // Simulate drop on the target tab + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {}, + clientX: dropX + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + targetTab.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + } + /** * Helper function to check if the tab bar for a specific pane is visible * @param {string} paneId - The pane ID ("first-pane" or "second-pane") @@ -560,31 +729,9 @@ define(function (require, exports, module) { await testWindow.closeAllFiles(); // Create and open multiple test files to work with - const testFiles = []; - for (let i = 0; i < 3; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/drag-drop-test-${i}.js`; - testFiles.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile(filePath, `// Drag drop test file ${i}`, FileSystem) - ); - } - - // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } - - // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() === testFiles.length; - }, - "All tabs to appear", - 1000 - ); + const testFiles = await createTestFiles(3, "drag-drop-test", "// Drag drop test file"); + await openTestFiles(testFiles); + await waitForTabs(testFiles); // Verify initial tab order const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); @@ -593,51 +740,8 @@ define(function (require, exports, module) { expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); - // Get the source and target tabs - const sourceTab = getTab(testFiles[0]); - const targetTab = getTab(testFiles[2]); - - // Simulate drag start on the first tab - const dragStartEvent = $.Event("dragstart", { - originalEvent: { - dataTransfer: { - setData: function () {}, - effectAllowed: "move" - } - } - }); - sourceTab.trigger(dragStartEvent); - - // Simulate dragenter on the last tab - const dragEnterEvent = $.Event("dragenter"); - targetTab.trigger(dragEnterEvent); - - // Simulate drag over on the last tab - const dragOverEvent = $.Event("dragover", { - originalEvent: { - dataTransfer: { - dropEffect: "move" - }, - clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge - }, - preventDefault: function () {} - }); - targetTab.trigger(dragOverEvent); - - // Simulate drop on the last tab - const dropEvent = $.Event("drop", { - originalEvent: { - dataTransfer: {}, - clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge - }, - preventDefault: function () {}, - stopPropagation: function () {} - }); - targetTab.trigger(dropEvent); - - // Simulate dragend to complete the operation - const dragEndEvent = $.Event("dragend"); - sourceTab.trigger(dragEndEvent); + // Simulate drag and drop from first tab to last tab + await simulateTabDragAndDrop(testFiles[0], testFiles[2], true); // Wait for the working set to update await awaitsFor( @@ -661,15 +765,7 @@ define(function (require, exports, module) { expect(finalWorkingSet[2].fullPath).toBe(testFiles[2]); // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - await awaitsForDone(promise, `Close file ${filePath}`); - } + await closeTestFiles(testFiles); }); it("should allow dragging and dropping a tab in between other tabs", async function () { @@ -680,31 +776,9 @@ define(function (require, exports, module) { await testWindow.closeAllFiles(); // Create and open multiple test files to work with - const testFiles = []; - for (let i = 0; i < 3; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/drag-between-test-${i}.js`; - testFiles.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile(filePath, `// Drag between test file ${i}`, FileSystem) - ); - } - - // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } - - // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() === testFiles.length; - }, - "All tabs to appear", - 1000 - ); + const testFiles = await createTestFiles(3, "drag-between-test", "// Drag between test file"); + await openTestFiles(testFiles); + await waitForTabs(testFiles); // Verify initial tab order const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); @@ -713,51 +787,8 @@ define(function (require, exports, module) { expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); - // Get the source and target tabs - we'll drag the last tab to between the first and second tabs - const sourceTab = getTab(testFiles[2]); // Last tab - const targetTab = getTab(testFiles[1]); // Middle tab - - // Simulate drag start on the last tab - const dragStartEvent = $.Event("dragstart", { - originalEvent: { - dataTransfer: { - setData: function () {}, - effectAllowed: "move" - } - } - }); - sourceTab.trigger(dragStartEvent); - - // Simulate dragenter on the middle tab - const dragEnterEvent = $.Event("dragenter"); - targetTab.trigger(dragEnterEvent); - - // Simulate drag over on the middle tab - position near the left edge to drop before it - const dragOverEvent = $.Event("dragover", { - originalEvent: { - dataTransfer: { - dropEffect: "move" - }, - clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge - }, - preventDefault: function () {} - }); - targetTab.trigger(dragOverEvent); - - // Simulate drop on the middle tab - const dropEvent = $.Event("drop", { - originalEvent: { - dataTransfer: {}, - clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge - }, - preventDefault: function () {}, - stopPropagation: function () {} - }); - targetTab.trigger(dropEvent); - - // Simulate dragend to complete the operation - const dragEndEvent = $.Event("dragend"); - sourceTab.trigger(dragEndEvent); + // Simulate drag and drop from last tab to before the middle tab + await simulateTabDragAndDrop(testFiles[2], testFiles[1], true); // Wait for the working set to update await awaitsFor( @@ -781,15 +812,7 @@ define(function (require, exports, module) { expect(finalWorkingSet[2].fullPath).toBe(testFiles[1]); // Middle tab should now be last // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - await awaitsForDone(promise, `Close file ${filePath}`); - } + await closeTestFiles(testFiles); }); it("should allow dragging a tab from one pane to another non-empty pane", async function () { @@ -803,51 +826,16 @@ define(function (require, exports, module) { MainViewManager.setLayoutScheme(1, 2); // Create test files for both panes - const firstPaneFiles = []; - for (let i = 0; i < 2; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-test-${i}.js`; - firstPaneFiles.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile(filePath, `// First pane test file ${i}`, FileSystem) - ); - } - - const secondPaneFiles = []; - for (let i = 0; i < 2; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/second-pane-test-${i}.js`; - secondPaneFiles.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile(filePath, `// Second pane test file ${i}`, FileSystem) - ); - } - - // Open files in the first pane - for (const filePath of firstPaneFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), - `Open file ${filePath} in first pane` - ); - } + const firstPaneFiles = await createTestFiles(2, "first-pane-test", "// First pane test file"); + const secondPaneFiles = await createTestFiles(2, "second-pane-test", "// Second pane test file"); - // Open files in the second pane - for (const filePath of secondPaneFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "second-pane" }), - `Open file ${filePath} in second pane` - ); - } + // Open files in both panes + await openTestFiles(firstPaneFiles, "first-pane"); + await openTestFiles(secondPaneFiles, "second-pane"); // Wait for all tabs to appear in both panes - await awaitsFor( - function () { - return ( - getPaneTabCount("first-pane") === firstPaneFiles.length && - getPaneTabCount("second-pane") === secondPaneFiles.length - ); - }, - "All tabs to appear in both panes", - 1000 - ); + await waitForTabs(firstPaneFiles, "first-pane"); + await waitForTabs(secondPaneFiles, "second-pane"); // Verify initial tab counts expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); @@ -947,31 +935,17 @@ define(function (require, exports, module) { } // Create test files for the first pane - const firstPaneFiles = []; - for (let i = 0; i < 2; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-empty-test-${i}.js`; - firstPaneFiles.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile(filePath, `// First pane empty test file ${i}`, FileSystem) - ); - } + const firstPaneFiles = await createTestFiles( + 2, + "first-pane-empty-test", + "// First pane empty test file" + ); // Open files in the first pane only - for (const filePath of firstPaneFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), - `Open file ${filePath} in first pane` - ); - } + await openTestFiles(firstPaneFiles, "first-pane"); // Wait for all tabs to appear in the first pane - await awaitsFor( - function () { - return getPaneTabCount("first-pane") === firstPaneFiles.length; - }, - "All tabs to appear in first pane", - 1000 - ); + await waitForTabs(firstPaneFiles, "first-pane"); // Ensure second pane is empty before proceeding await awaitsFor( @@ -1073,59 +1047,26 @@ define(function (require, exports, module) { }); it("should add tabs when files are added to the working set", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - - // Wait for the tab bar to update - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab for first file to appear", - 1000 - ); + // Open the first test file and wait for its tab to appear + await openTestFiles([testFilePath]); + await waitForTabs([testFilePath]); // Verify the tab exists expect(tabExists(testFilePath)).toBe(true); expect(getTabCount()).toBe(1); - // Open the second test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - - // Wait for the tab bar to update - await awaitsFor( - function () { - return tabExists(testFilePath2); - }, - "Tab for second file to appear", - 1000 - ); + // Open the second test file and wait for its tab to appear + await openTestFiles([testFilePath2]); + await waitForTabs([testFilePath2]); // Verify both tabs exist expect(tabExists(testFilePath)).toBe(true); expect(tabExists(testFilePath2)).toBe(true); expect(getTabCount()).toBe(2); - // Open the third test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for the tab bar to update - await awaitsFor( - function () { - return tabExists(testFilePath3); - }, - "Tab for third file to appear", - 1000 - ); + // Open the third test file and wait for its tab to appear + await openTestFiles([testFilePath3]); + await waitForTabs([testFilePath3]); // Verify all three tabs exist expect(tabExists(testFilePath)).toBe(true); @@ -1135,43 +1076,16 @@ define(function (require, exports, module) { }); it("should remove tabs when files are removed from the working set", async function () { - // Open all three test files like in previous test - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); + // Open all three test files + const testFiles = [testFilePath, testFilePath2, testFilePath3]; + await openTestFiles(testFiles); + await waitForTabs(testFiles); // Verify all three tabs exist expect(getTabCount()).toBe(3); // Close the second test file - const fileToClose2 = FileSystem.getFileForPath(testFilePath2); - const promise2 = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose2 }); - - // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - - await awaitsForDone(promise2, "Close second test file"); + await closeTestFiles([testFilePath2]); // Wait for the tab to disappear await awaitsFor( @@ -1189,16 +1103,7 @@ define(function (require, exports, module) { expect(getTabCount()).toBe(2); // Close the first test file - const fileToClose1 = FileSystem.getFileForPath(testFilePath); - const promise1 = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose1 }); - - // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - - await awaitsForDone(promise1, "Close first test file"); + await closeTestFiles([testFilePath]); // Wait for the tab to disappear await awaitsFor( @@ -1216,16 +1121,7 @@ define(function (require, exports, module) { expect(getTabCount()).toBe(1); // Close the third test file - const fileToClose3 = FileSystem.getFileForPath(testFilePath3); - const promise3 = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose3 }); - - // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - - await awaitsForDone(promise3, "Close third test file"); + await closeTestFiles([testFilePath3]); // Wait for the tab to disappear await awaitsFor( @@ -1253,89 +1149,36 @@ define(function (require, exports, module) { await testWindow.closeAllFiles(); // Open all three test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); + const testFiles = [testFilePath, testFilePath2, testFilePath3]; + await openTestFiles(testFiles); + await waitForTabs(testFiles); }); it("should change active tab when switching files in the working set", async function () { - // Switch to the first file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Switch to first file" - ); - - // Wait for the tab to become active - await awaitsFor( - function () { - return isTabActive(testFilePath); - }, - "First tab to become active", - 1000 - ); + // Helper function to switch to a file and verify it's active + async function switchToFileAndVerify(filePath, description) { + // Switch to the file + await openTestFiles([filePath]); - // Verify the first tab is active and others are not - expect(isTabActive(testFilePath)).toBe(true); - expect(isTabActive(testFilePath2)).toBe(false); - expect(isTabActive(testFilePath3)).toBe(false); - - // Switch to the second file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Switch to second file" - ); - - // Wait for the tab to become active - await awaitsFor( - function () { - return isTabActive(testFilePath2); - }, - "Second tab to become active", - 1000 - ); - - // Verify the second tab is active and others are not - expect(isTabActive(testFilePath)).toBe(false); - expect(isTabActive(testFilePath2)).toBe(true); - expect(isTabActive(testFilePath3)).toBe(false); - - // Switch to the third file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Switch to third file" - ); + // Wait for the tab to become active + await awaitsFor( + function () { + return isTabActive(filePath); + }, + `${description} to become active`, + 1000 + ); - // Wait for the tab to become active - await awaitsFor( - function () { - return isTabActive(testFilePath3); - }, - "Third tab to become active", - 1000 - ); + // Verify this tab is active and others are not + expect(isTabActive(testFilePath)).toBe(filePath === testFilePath); + expect(isTabActive(testFilePath2)).toBe(filePath === testFilePath2); + expect(isTabActive(testFilePath3)).toBe(filePath === testFilePath3); + } - // Verify the third tab is active and others are not - expect(isTabActive(testFilePath)).toBe(false); - expect(isTabActive(testFilePath2)).toBe(false); - expect(isTabActive(testFilePath3)).toBe(true); + // Test switching to each file + await switchToFileAndVerify(testFilePath, "First tab"); + await switchToFileAndVerify(testFilePath2, "Second tab"); + await switchToFileAndVerify(testFilePath3, "Third tab"); }); it("should display active tab correctly based on the active file in the working set", async function () { @@ -1349,10 +1192,7 @@ define(function (require, exports, module) { expect(isTabActive(activeFile.fullPath)).toBe(true); // Switch to a different file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Switch to second file" - ); + await openTestFiles([testFilePath2]); // Get the new active file const newActiveFile = MainViewManager.getCurrentlyViewedFile(); @@ -1421,29 +1261,13 @@ define(function (require, exports, module) { it("should show overflow button when there are too many tabs to fit", async function () { // Create several test files to ensure overflow - const testFiles = []; - for (let i = 0; i < 15; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); - } + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } + await openTestFiles(testFiles); // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() >= testFiles.length; - }, - "All tabs to appear", - 1000 - ); + await waitForTabs(testFiles); // Wait for the overflow button to appear await awaitsFor( @@ -1473,42 +1297,18 @@ define(function (require, exports, module) { expect(visibleTabs + hiddenTabs).toBe(testFiles.length); // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - await awaitsForDone(promise, `Close file ${filePath}`); - } + await closeTestFiles(testFiles); }); it("should display dropdown with hidden tabs when overflow button is clicked", async function () { // Create several test files to ensure overflow - const testFiles = []; - for (let i = 0; i < 15; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); - } + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } + await openTestFiles(testFiles); // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() >= testFiles.length; - }, - "All tabs to appear", - 1000 - ); + await waitForTabs(testFiles); // Wait for the overflow button to appear await awaitsFor( @@ -1561,42 +1361,18 @@ define(function (require, exports, module) { ); // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - await awaitsForDone(promise, `Close file ${filePath}`); - } + await closeTestFiles(testFiles); }); it("should make tab visible and file active when clicking on item in overflow dropdown", async function () { // Create several test files to ensure overflow - const testFiles = []; - for (let i = 0; i < 15; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); - } + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } + await openTestFiles(testFiles); // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() >= testFiles.length; - }, - "All tabs to appear", - 1000 - ); + await waitForTabs(testFiles); // Wait for the overflow button to appear await awaitsFor( @@ -1661,42 +1437,18 @@ define(function (require, exports, module) { expect(isTabVisible(testHiddenFile)).toBe(true); // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - await awaitsForDone(promise, `Close file ${filePath}`); - } + await closeTestFiles(testFiles); }); it("should scroll tab bar to make selected file visible when selecting from working set", async function () { // Create several test files to ensure overflow - const testFiles = []; - for (let i = 0; i < 15; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/overflow-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Overflow test file ${i}`, FileSystem)); - } + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } + await openTestFiles(testFiles); // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() >= testFiles.length; - }, - "All tabs to appear", - 1000 - ); + await waitForTabs(testFiles); // Wait for the overflow button to appear await awaitsFor( @@ -1751,15 +1503,7 @@ define(function (require, exports, module) { expect(isTabVisible(testHiddenFile)).toBe(true); // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - await awaitsForDone(promise, `Close file ${filePath}`); - } + await closeTestFiles(testFiles); }); }); @@ -2055,10 +1799,7 @@ define(function (require, exports, module) { $closeButton.click(); // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); // Wait for the tab to disappear await awaitsFor( @@ -2184,10 +1925,7 @@ define(function (require, exports, module) { $closeTabOption.click(); // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); // Verify the tab is closed await awaitsFor( @@ -2256,10 +1994,7 @@ define(function (require, exports, module) { $closeTabsToRightOption.click(); // Cancel any save dialogs that might appear - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); // Verify tabs to the right are closed await awaitsFor( @@ -2334,10 +2069,7 @@ define(function (require, exports, module) { $closeTabsToLeftOption.click(); // Cancel any save dialogs that might appear - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); // Verify tabs to the left are closed await awaitsFor( @@ -2428,10 +2160,7 @@ define(function (require, exports, module) { $closeSavedTabsOption.click(); // Cancel any save dialogs that might appear - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); // Verify only the dirty tab remains await awaitsFor( @@ -2513,10 +2242,7 @@ define(function (require, exports, module) { $closeAllTabsOption.click(); // Cancel any save dialogs that might appear - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); // Verify all tabs are closed await awaitsFor( @@ -2581,10 +2307,7 @@ define(function (require, exports, module) { for (const filePath of testFiles) { const fileToClose = FileSystem.getFileForPath(filePath); const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); await awaitsForDone(promise, `Close file ${filePath}`); } }); @@ -2625,10 +2348,7 @@ define(function (require, exports, module) { for (const filePath of testFiles) { const fileToClose = FileSystem.getFileForPath(filePath); const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); await awaitsForDone(promise, `Close file ${filePath}`); } }); @@ -2681,10 +2401,7 @@ define(function (require, exports, module) { for (const filePath of testFiles) { const fileToClose = FileSystem.getFileForPath(filePath); const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); await awaitsForDone(promise, `Close file ${filePath}`); } }); @@ -2941,10 +2658,7 @@ define(function (require, exports, module) { }); // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); await awaitsForDone(promise, "Close file in second pane"); @@ -3011,10 +2725,7 @@ define(function (require, exports, module) { }); // Cancel the save dialog if it appears - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); + cancelSaveDialog(); await awaitsForDone(promise, "Close file in second pane"); From d91e4a1ef037ad71cf1df876e7207e63ab0f756c Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 14:15:49 +0530 Subject: [PATCH 20/26] fix: remove await timers from tests --- test/spec/Extn-Tabbar-integ-test.js | 87 +++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 7466099782..14f30bb943 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -718,7 +718,13 @@ define(function (require, exports, module) { MainViewManager.setLayoutScheme(1, 1); // Wait for cleanup to complete - await awaits(100); + await awaitsFor( + function () { + return MainViewManager.getPaneCount() === 1 && getTabCount() === 0; + }, + "Cleanup to complete with single pane and no tabs", + 1000 + ); }); it("should allow dragging and dropping a tab to the beginning of the tab bar", async function () { @@ -923,7 +929,13 @@ define(function (require, exports, module) { MainViewManager.setLayoutScheme(1, 2); // Wait for layout to settle and ensure second pane is empty - await awaits(100); + await awaitsFor( + function () { + return MainViewManager.getPaneCount() === 2; + }, + "Layout to settle with two panes", + 1000 + ); // Force close any files that might be open in the second pane const secondPaneWorkingSet = MainViewManager.getWorkingSet("second-pane"); @@ -934,6 +946,17 @@ define(function (require, exports, module) { ); } + // Wait for the second pane to actually be empty after cleanup + await awaitsFor( + function () { + return getPaneTabCount("second-pane") === 0; + }, + "Second pane to be cleaned up", + 2000 + ); + + expect(getPaneTabCount("second-pane")).toBe(0); + // Create test files for the first pane const firstPaneFiles = await createTestFiles( 2, @@ -947,6 +970,8 @@ define(function (require, exports, module) { // Wait for all tabs to appear in the first pane await waitForTabs(firstPaneFiles, "first-pane"); + expect(getPaneTabCount("second-pane")).toBe(0); + // Ensure second pane is empty before proceeding await awaitsFor( function () { @@ -1043,7 +1068,13 @@ define(function (require, exports, module) { await testWindow.closeAllFiles(); // Wait for the tab bar to update - await awaits(300); + await awaitsFor( + function () { + return $("#phoenix-tab-bar").length > 0 && getTabCount() === 0; + }, + "Tab bar to update with no tabs", + 1000 + ); }); it("should add tabs when files are added to the working set", async function () { @@ -1185,8 +1216,14 @@ define(function (require, exports, module) { // Get the currently active file const activeFile = MainViewManager.getCurrentlyViewedFile(); - // just a small timer because tab bar gets recreated - await awaits(100); + // Wait for tab bar to be recreated and reflect the active file + await awaitsFor( + function () { + return $("#phoenix-tab-bar").length > 0 && activeFile && isTabActive(activeFile.fullPath); + }, + "Tab bar to be recreated and show active file", + 1000 + ); // Verify the tab for the active file is active expect(isTabActive(activeFile.fullPath)).toBe(true); @@ -1197,7 +1234,14 @@ define(function (require, exports, module) { // Get the new active file const newActiveFile = MainViewManager.getCurrentlyViewedFile(); - await awaits(100); + // Wait for tab bar to update with the new active file + await awaitsFor( + function () { + return newActiveFile && isTabActive(newActiveFile.fullPath); + }, + "Tab bar to update with new active file", + 1000 + ); // Verify the tab for the new active file is active expect(isTabActive(newActiveFile.fullPath)).toBe(true); @@ -1233,8 +1277,25 @@ define(function (require, exports, module) { // Verify the correct file is loaded in the editor expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(filePath); } - // add a small timer to make sure that the tab bar is properly loaded - await awaits(100); + // Wait for the tab bar to be properly loaded + await awaitsFor( + function () { + return $("#phoenix-tab-bar").length > 0 && getTabCount() === 3; + }, + "Tab bar to be properly loaded with all tabs", + 1000 + ); + + // Wait for the third file to become active + await awaitsFor( + function () { + return isTabActive(testFilePath3) && + MainViewManager.getCurrentlyViewedFile() && + MainViewManager.getCurrentlyViewedFile().fullPath === testFilePath3; + }, + "Third file to become active", + 2000 + ); // Initially, verify the third file is active (last opened) expect(isTabActive(testFilePath3)).toBe(true); @@ -1716,8 +1777,14 @@ define(function (require, exports, module) { // Trigger a Git status update testWindow.phoenixGitEvents.EventEmitter.emit("GIT_FILE_STATUS_CHANGED"); - // Wait for the tabs to update - await awaits(300); + // Wait for the tabs to update with Git status + await awaitsFor( + function () { + return hasGitStatus(testFilePath) && hasGitStatus(testFilePath2); + }, + "Tabs to update with Git status", + 1000 + ); // Verify the first file has the git-new class const $tab1 = getTab(testFilePath); From f10d979d07db381a1faaeeddeaf10c50b3eafe12 Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 14:45:20 +0530 Subject: [PATCH 21/26] fix: add small floating point tolerance as tests were failing in chromium --- test/spec/Extn-Tabbar-integ-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 14f30bb943..0a9bc045c9 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -3044,8 +3044,8 @@ define(function (require, exports, module) { wheelEventRight.originalEvent = { deltaY: 500 }; // Large scroll right $tabBar.trigger(wheelEventRight); - // Should not exceed maximum scroll - expect($tabBar.scrollLeft()).toBeLessThanOrEqual(maxScrollLeft); + // Should not exceed maximum scroll (a small floating point tolerance) + expect($tabBar.scrollLeft()).toBeLessThanOrEqual(maxScrollLeft + 1); // Scroll far to the left $tabBar.scrollLeft(-1000); // Try to scroll beyond minimum @@ -3055,8 +3055,8 @@ define(function (require, exports, module) { wheelEventLeft.originalEvent = { deltaY: -500 }; // Large scroll left $tabBar.trigger(wheelEventLeft); - // Should not go below 0 - expect($tabBar.scrollLeft()).toBeGreaterThanOrEqual(0); + // Should not go below 0 (a small floating point tolerance) + expect($tabBar.scrollLeft()).toBeGreaterThanOrEqual(-1); }); }); }); From 7099453a30fb2aac2b7e7ce3600babeda617e95f Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 15:22:22 +0530 Subject: [PATCH 22/26] fix: dragging in empty pane test fails because it contained some unexpected tabs --- test/spec/Extn-Tabbar-integ-test.js | 150 +- test/spec/Extn-Tabbar-integ-test.js~ | 3063 ++++++++++++++++++++++++++ 2 files changed, 3140 insertions(+), 73 deletions(-) create mode 100644 test/spec/Extn-Tabbar-integ-test.js~ diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 0a9bc045c9..f142f78c95 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -928,7 +928,7 @@ define(function (require, exports, module) { // Set up a horizontal split view (two columns) MainViewManager.setLayoutScheme(1, 2); - // Wait for layout to settle and ensure second pane is empty + // Wait for layout to settle await awaitsFor( function () { return MainViewManager.getPaneCount() === 2; @@ -937,25 +937,9 @@ define(function (require, exports, module) { 1000 ); - // Force close any files that might be open in the second pane - const secondPaneWorkingSet = MainViewManager.getWorkingSet("second-pane"); - for (const file of secondPaneWorkingSet) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: file, paneId: "second-pane" }), - `Force close file ${file.fullPath} in second pane` - ); - } - - // Wait for the second pane to actually be empty after cleanup - await awaitsFor( - function () { - return getPaneTabCount("second-pane") === 0; - }, - "Second pane to be cleaned up", - 2000 - ); - - expect(getPaneTabCount("second-pane")).toBe(0); + // Verify both panes are empty + expect(MainViewManager.getWorkingSet("first-pane").length).toBe(0); + expect(MainViewManager.getWorkingSet("second-pane").length).toBe(0); // Create test files for the first pane const firstPaneFiles = await createTestFiles( @@ -970,67 +954,85 @@ define(function (require, exports, module) { // Wait for all tabs to appear in the first pane await waitForTabs(firstPaneFiles, "first-pane"); - expect(getPaneTabCount("second-pane")).toBe(0); + // Verify initial state: first pane has tabs, second pane is empty + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); - // Ensure second pane is empty before proceeding - await awaitsFor( - function () { - return getPaneTabCount("second-pane") === 0 && !isTabBarVisible("second-pane"); - }, - "Second pane to be empty", - 1000 - ); + // If the second pane has tabs, log what they are and close them + if (getPaneTabCount("second-pane") > 0) { + const secondPaneWorkingSet = MainViewManager.getWorkingSet("second-pane"); + for (const file of secondPaneWorkingSet) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: file, paneId: "second-pane" }), + `Force close file ${file.fullPath} in second pane` + ); + } + + // Wait for the second pane to be empty + await awaitsFor( + function () { + return getPaneTabCount("second-pane") === 0; + }, + "Second pane to be empty after cleanup", + 2000 + ); + } - // Verify initial tab counts - expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); expect(getPaneTabCount("second-pane")).toBe(0); expect(isTabBarVisible("second-pane")).toBe(false); // Get the source tab from the first pane const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); + expect(sourceTab.length).toBe(1, "Source tab should exist in the first pane"); // Get the empty pane content area as the drop target const emptyPaneTarget = $("#second-pane .pane-content"); - - // Simulate drag start on the source tab - const dragStartEvent = $.Event("dragstart", { - originalEvent: { - dataTransfer: { - setData: function () {}, - effectAllowed: "move" - } - } - }); - sourceTab.trigger(dragStartEvent); - - // Simulate dragenter on the empty pane - const dragEnterEvent = $.Event("dragenter"); - emptyPaneTarget.trigger(dragEnterEvent); - - // Simulate drag over on the empty pane - const dragOverEvent = $.Event("dragover", { - originalEvent: { - dataTransfer: { - dropEffect: "move" + expect(emptyPaneTarget.length).toBe(1, "Empty pane target should exist"); + + // Simulate drag and drop + try { + // Simulate drag start + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } } - }, - preventDefault: function () {} - }); - emptyPaneTarget.trigger(dragOverEvent); - - // Simulate drop on the empty pane - const dropEvent = $.Event("drop", { - originalEvent: { - dataTransfer: {} - }, - preventDefault: function () {}, - stopPropagation: function () {} - }); - emptyPaneTarget.trigger(dropEvent); + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter + const dragEnterEvent = $.Event("dragenter"); + emptyPaneTarget.trigger(dragEnterEvent); + + // Simulate dragover + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + } + }, + preventDefault: function () {} + }); + emptyPaneTarget.trigger(dragOverEvent); - // Simulate dragend to complete the operation - const dragEndEvent = $.Event("dragend"); - sourceTab.trigger(dragEndEvent); + // Simulate drop + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {} + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + emptyPaneTarget.trigger(dropEvent); + + // Simulate dragend + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + } catch (e) { + console.error("Error during drag and drop simulation:", e); + throw e; + } // Wait for the tab to move to the second pane await awaitsFor( @@ -1042,7 +1044,7 @@ define(function (require, exports, module) { ); }, "Tab to move from first pane to second pane and tab bar to appear", - 1000 + 2000 ); // Verify the tab counts after the drag and drop @@ -1289,9 +1291,11 @@ define(function (require, exports, module) { // Wait for the third file to become active await awaitsFor( function () { - return isTabActive(testFilePath3) && - MainViewManager.getCurrentlyViewedFile() && - MainViewManager.getCurrentlyViewedFile().fullPath === testFilePath3; + return ( + isTabActive(testFilePath3) && + MainViewManager.getCurrentlyViewedFile() && + MainViewManager.getCurrentlyViewedFile().fullPath === testFilePath3 + ); }, "Third file to become active", 2000 diff --git a/test/spec/Extn-Tabbar-integ-test.js~ b/test/spec/Extn-Tabbar-integ-test.js~ new file mode 100644 index 0000000000..0a9bc045c9 --- /dev/null +++ b/test/spec/Extn-Tabbar-integ-test.js~ @@ -0,0 +1,3063 @@ +/* + * GNU AGPL-3.0 License + * + * Copyright (c) 2021 - present core.ai . All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License + * for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0. + * + */ + +/*global describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, awaitsFor, awaitsForDone, awaits, jsPromise */ + +define(function (require, exports, module) { + const SpecRunnerUtils = require("spec/SpecRunnerUtils"); + + describe("integration:TabBar", function () { + let testWindow, + PreferencesManager, + $, + FileSystem, + MainViewManager, + CommandManager, + Commands, + DocumentManager, + Strings; + let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; + + /** + * Helper function to create multiple test files + * @param {number} count - Number of files to create + * @param {string} prefix - Prefix for the file names + * @param {string} content - Content template for the files (will be appended with file index) + * @returns {Promise} - Array of file paths + */ + async function createTestFiles(count, prefix, content) { + const testFiles = []; + for (let i = 0; i < count; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/${prefix}-${i}.js`; + testFiles.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile( + filePath, + content ? `${content} ${i}` : `// ${prefix} test file ${i}`, + FileSystem + ) + ); + } + return testFiles; + } + + /** + * Helper function to open multiple files + * @param {string[]} filePaths - Array of file paths to open + * @param {string} [paneId] - Optional pane ID to open the files in + * @returns {Promise} + */ + async function openTestFiles(filePaths, paneId) { + for (const filePath of filePaths) { + const options = { fullPath: filePath }; + if (paneId) { + options.paneId = paneId; + } + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, options), + `Open file ${filePath}${paneId ? ` in ${paneId}` : ""}` + ); + } + } + + /** + * Helper function to wait for tabs to appear + * @param {string[]} filePaths - Array of file paths to wait for + * @param {string} [paneId] - Optional pane ID to check for tabs + * @returns {Promise} + */ + async function waitForTabs(filePaths, paneId) { + if (paneId) { + // Wait for tabs to appear in the specified pane + await awaitsFor( + function () { + return getPaneTabCount(paneId) >= filePaths.length; + }, + `All tabs to appear in ${paneId}`, + 1000 + ); + } else if (filePaths.length === 1) { + // Wait for a single tab to appear + await awaitsFor( + function () { + return tabExists(filePaths[0]); + }, + `Tab for ${filePaths[0]} to appear`, + 1000 + ); + } else { + // Wait for multiple tabs to appear + await awaitsFor( + function () { + return getTabCount() >= filePaths.length && filePaths.every((path) => tabExists(path)); + }, + "All tabs to appear", + 1000 + ); + } + } + + /** + * Helper function to cancel the save dialog + * @returns {void} + */ + function cancelSaveDialog() { + testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( + testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, + testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE + ); + } + + /** + * Helper function to close files + * @param {string[]} filePaths - Array of file paths to close + * @param {string} [paneId] - Optional pane ID to close the files from + * @returns {Promise} + */ + async function closeTestFiles(filePaths, paneId) { + for (const filePath of filePaths) { + const fileToClose = FileSystem.getFileForPath(filePath); + const options = { file: fileToClose }; + if (paneId) { + options.paneId = paneId; + } + const promise = CommandManager.execute(Commands.FILE_CLOSE, options); + cancelSaveDialog(); + await awaitsForDone(promise, `Close file ${filePath}`); + } + } + + /** + * Helper function to simulate drag and drop between tabs + * @param {string} sourceFilePath - Path of the source file to drag + * @param {string} targetFilePath - Path of the target file to drop onto + * @param {boolean} [dropBefore=true] - Whether to drop before the target (true) or after (false) + * @returns {Promise} + */ + async function simulateTabDragAndDrop(sourceFilePath, targetFilePath, dropBefore = true) { + // Get the source and target tabs + const sourceTab = getTab(sourceFilePath); + const targetTab = getTab(targetFilePath); + + // Simulate drag start on the source tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the target tab + const dragEnterEvent = $.Event("dragenter"); + targetTab.trigger(dragEnterEvent); + + // Simulate drag over on the target tab + const targetRect = targetTab[0].getBoundingClientRect(); + const dropX = dropBefore + ? targetRect.left + 5 // Position near the left edge to drop before + : targetRect.right - 5; // Position near the right edge to drop after + + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + }, + clientX: dropX + }, + preventDefault: function () {} + }); + targetTab.trigger(dragOverEvent); + + // Simulate drop on the target tab + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {}, + clientX: dropX + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + targetTab.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + } + + /** + * Helper function to check if the tab bar for a specific pane is visible + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {boolean} - True if the tab bar is visible, false otherwise + */ + function isTabBarVisible(paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).is(":visible"); + } + + /** + * Helper function to get the tab count for a specific pane + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {number} - The number of tabs in the pane + */ + function getPaneTabCount(paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).find(".tab").length; + } + + /** + * Helper function to check if a tab for a specific file exists in a specific pane + * @param {string} filePath - The path of the file to check + * @param {string} paneId - The pane ID ("first-pane" or "second-pane") + * @returns {boolean} - True if the tab exists in the pane, false otherwise + */ + function tabExistsInPane(filePath, paneId) { + const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; + return $(tabBarId).find(`.tab[data-path="${filePath}"]`).length > 0; + } + + beforeAll(async function () { + // Create the test window + testWindow = await SpecRunnerUtils.createTestWindowAndRun(); + // Get reference to all the required modules + $ = testWindow.$; + PreferencesManager = testWindow.brackets.test.PreferencesManager; + FileSystem = testWindow.brackets.test.FileSystem; + MainViewManager = testWindow.brackets.test.MainViewManager; + CommandManager = testWindow.brackets.test.CommandManager; + Commands = testWindow.brackets.test.Commands; + DocumentManager = testWindow.brackets.test.DocumentManager; + Strings = testWindow.Strings; + + // Create test files + testFilePath = SpecRunnerUtils.getTempDirectory() + "/tabbar-test.js"; + testFilePath2 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test2.js"; + testFilePath3 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test3.js"; + + // Create files with the same name in different directories for testing duplicate name handling + testDuplicateDir1 = SpecRunnerUtils.getTempDirectory() + "/dir1"; + testDuplicateDir2 = SpecRunnerUtils.getTempDirectory() + "/dir2"; + testDuplicateName = "duplicate.js"; + + await SpecRunnerUtils.createTempDirectory(); + await SpecRunnerUtils.ensureExistsDirAsync(testDuplicateDir1); + await SpecRunnerUtils.ensureExistsDirAsync(testDuplicateDir2); + + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath, "// Test file 1 for TabBar", FileSystem)); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath2, "// Test file 2 for TabBar", FileSystem)); + await jsPromise(SpecRunnerUtils.createTextFile(testFilePath3, "// Test file 3 for TabBar", FileSystem)); + await jsPromise( + SpecRunnerUtils.createTextFile( + testDuplicateDir1 + "/" + testDuplicateName, + "// Duplicate file 1", + FileSystem + ) + ); + await jsPromise( + SpecRunnerUtils.createTextFile( + testDuplicateDir2 + "/" + testDuplicateName, + "// Duplicate file 2", + FileSystem + ) + ); + + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + }, 30000); + + afterAll(async function () { + // Close all files without prompting to save + await testWindow.closeAllFiles(); + + testWindow = null; + await SpecRunnerUtils.closeTestWindow(); + await SpecRunnerUtils.removeTempDirectory(); + }, 30000); + + /** + * Helper function to check if a tab for a specific file exists in the tab bar + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab exists, false otherwise + */ + function tabExists(filePath) { + return $(`.tab[data-path="${filePath}"]`).length > 0; + } + + /** + * Helper function to count the number of tabs in the tab bar + * @returns {number} - The number of tabs + */ + function getTabCount() { + return $(".tab").length; + } + + /** + * Helper function to check if a tab for a specific file is active + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab is active, false otherwise + */ + function isTabActive(filePath) { + return $(`.tab[data-path="${filePath}"].active`).length > 0; + } + + /** + * Helper function to get the tab element for a specific file + * @param {string} filePath - The path of the file + * @returns {jQuery} - The tab element + */ + function getTab(filePath) { + return $(`.tab[data-path="${filePath}"]`); + } + + /** + * Helper function to check if a tab has a dirty indicator + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a dirty indicator, false otherwise + */ + function isTabDirty(filePath) { + return getTab(filePath).hasClass("dirty"); + } + + /** + * Helper function to get the tab name element for a specific file + * @param {string} filePath - The path of the file + * @returns {jQuery} - The tab name element + */ + function getTabName(filePath) { + return getTab(filePath).find(".tab-name"); + } + + /** + * Helper function to check if a tab has a directory name displayed + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a directory name, false otherwise + */ + function hasDirectoryName(filePath) { + return getTab(filePath).find(".tab-dirname").length > 0; + } + + /** + * Helper function to get the directory name displayed in a tab + * @param {string} filePath - The path of the file + * @returns {string} - The directory name or empty string if not found + */ + function getDirectoryName(filePath) { + const $dirName = getTab(filePath).find(".tab-dirname"); + return $dirName.length ? $dirName.text() : ""; + } + + /** + * Helper function to check if a tab has a file icon + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a file icon, false otherwise + */ + function hasFileIcon(filePath) { + return getTab(filePath).find(".tab-icon i").length > 0; + } + + /** + * Helper function to check if a tab has a git status indicator + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a git status indicator, false otherwise + */ + function hasGitStatus(filePath) { + return getTab(filePath).hasClass("git-new") || getTab(filePath).hasClass("git-modified"); + } + + /** + * Helper function to get the tooltip (title attribute) of a tab + * @param {string} filePath - The path of the file + * @returns {string} - The tooltip text + */ + function getTabTooltip(filePath) { + return getTab(filePath).attr("title") || ""; + } + + /** + * Helper function to check if a tab has a close button + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab has a close button, false otherwise + */ + function hasCloseButton(filePath) { + return getTab(filePath).find(".tab-close").length > 0; + } + + /** + * Helper function to check if the overflow button is visible + * @returns {boolean} - True if the overflow button is visible, false otherwise + */ + function isOverflowButtonVisible() { + return $("#overflow-button").is(":visible"); + } + + /** + * Helper function to get the overflow button element + * @returns {jQuery} - The overflow button element + */ + function getOverflowButton() { + return $("#overflow-button"); + } + + /** + * Helper function to check if a tab is visible in the tab bar (not hidden by overflow) + * @param {string} filePath - The path of the file to check + * @returns {boolean} - True if the tab is visible, false otherwise + */ + function isTabVisible(filePath) { + const $tab = getTab(filePath); + if (!$tab.length) { + return false; + } + + const $tabBar = $("#phoenix-tab-bar"); + const tabBarRect = $tabBar[0].getBoundingClientRect(); + const tabRect = $tab[0].getBoundingClientRect(); + + // A tab is considered visible if it is completely within the tab bar's visible area + // with a small margin of error (2px) + return tabRect.left >= tabBarRect.left && tabRect.right <= tabBarRect.right + 2; + } + + /** + * Helper function to get the overflow dropdown menu + * @returns {jQuery} - The overflow dropdown menu element + */ + function getOverflowDropdown() { + return $(".dropdown-overflow-menu"); + } + + /** + * Helper function to get the items in the overflow dropdown + * @returns {jQuery} - The overflow dropdown items + */ + function getOverflowDropdownItems() { + return $(".dropdown-overflow-menu .dropdown-tab-item"); + } + + /** + * Helper function to get a specific item in the overflow dropdown by file path + * @param {string} filePath - The path of the file to find + * @returns {jQuery} - The dropdown item element + */ + function getOverflowDropdownItem(filePath) { + return $(`.dropdown-overflow-menu .dropdown-tab-item[data-tab-path="${filePath}"]`); + } + + describe("Visibility", function () { + it("should show tab bar when the feature is enabled", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Wait for the tab bar to become visible + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become visible", + 1000 + ); + + // Verify the tab bar is visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(true); + }); + + it("should hide tab bar when the feature is disabled", async function () { + // Disable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: false, numberOfTabs: -1 }); + + // Wait for the tab bar to become hidden + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become hidden", + 1000 + ); + + // Verify the tab bar is not visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + }); + + it("should show working set when the option is enabled", async function () { + // Enable the working set feature + PreferencesManager.set("showWorkingSet", true); + + // Wait for the working set to become visible + await awaitsFor( + function () { + return !$("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become visible", + 1000 + ); + + // Verify the working set is visible + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); + }); + + it("should hide working set when the option is disabled", async function () { + // Disable the working set feature + PreferencesManager.set("showWorkingSet", false); + + // Wait for the working set to become hidden + await awaitsFor( + function () { + return $("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become hidden", + 1000 + ); + + // Verify the working set is not visible + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); + }); + }); + + describe("Configure Working Set Button", function () { + it("should have a working set configuration button in the sidebar", function () { + // Verify the button exists + const $configButton = $(".working-set-splitview-btn"); + expect($configButton.length).toBe(1); + }); + + it("should open a menu with 'Show working set' and 'Show file tab bar' options when clicked", async function () { + // Click the configure working set button + const $configButton = $(".working-set-splitview-btn"); + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Verify the menu contains the expected options + const $menu = $(".dropdown-menu:visible"); + const showWorkingSetItem = $menu.find("li a[id$='cmd.toggleShowWorkingSet']"); + const showFileTabsItem = $menu.find("li a[id$='cmd.toggleShowFileTabs']"); + + expect(showWorkingSetItem.length).toBe(1); + expect(showFileTabsItem.length).toBe(1); + + // Clean up - close the menu + $("body").click(); + }); + + it("should toggle working set visibility when 'Show working set' option is clicked", async function () { + // First, ensure working set is visible + PreferencesManager.set("showWorkingSet", true); + await awaitsFor( + function () { + return !$("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become visible", + 1000 + ); + + // Click the configure working set button + const $configButton = $(".working-set-splitview-btn"); + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show working set" option + const $menu = $(".dropdown-menu:visible"); + const showWorkingSetItem = $menu.find("li a[id$='cmd.toggleShowWorkingSet']"); + showWorkingSetItem.click(); + + // Wait for the working set to become hidden + await awaitsFor( + function () { + return $("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become hidden", + 1000 + ); + + // Verify the working set is hidden + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); + + // Click the configure working set button again + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show working set" option again + const $menu2 = $(".dropdown-menu:visible"); + const showWorkingSetItem2 = $menu2.find("li a[id$='cmd.toggleShowWorkingSet']"); + showWorkingSetItem2.click(); + + // Wait for the working set to become visible + await awaitsFor( + function () { + return !$("#working-set-list-container").hasClass("working-set-hidden"); + }, + "Working set to become visible", + 1000 + ); + + // Verify the working set is visible + expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); + }); + + it("should toggle tab bar visibility when 'Show file tab bar' option is clicked", async function () { + // First, ensure tab bar is visible + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become visible", + 1000 + ); + + // Click the configure working set button + const $configButton = $(".working-set-splitview-btn"); + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show file tab bar" option + const $menu = $(".dropdown-menu:visible"); + const showFileTabsItem = $menu.find("li a[id$='cmd.toggleShowFileTabs']"); + showFileTabsItem.click(); + + // Wait for the tab bar to become hidden + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become hidden", + 1000 + ); + + // Verify the tab bar is hidden + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + + // Click the configure working set button again + $configButton.click(); + + // Wait for the menu to appear + await awaitsFor( + function () { + return $(".dropdown-menu:visible").length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Click the "Show file tab bar" option again + const $menu2 = $(".dropdown-menu:visible"); + const showFileTabsItem2 = $menu2.find("li a[id$='cmd.toggleShowFileTabs']"); + showFileTabsItem2.click(); + + // Wait for the tab bar to become visible + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to become visible", + 1000 + ); + + // Verify the tab bar is visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(true); + }); + }); + + describe("Drag and Drop", function () { + beforeEach(async function () { + // Close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + + // Wait for cleanup to complete + await awaitsFor( + function () { + return MainViewManager.getPaneCount() === 1 && getTabCount() === 0; + }, + "Cleanup to complete with single pane and no tabs", + 1000 + ); + }); + + it("should allow dragging and dropping a tab to the beginning of the tab bar", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Create and open multiple test files to work with + const testFiles = await createTestFiles(3, "drag-drop-test", "// Drag drop test file"); + await openTestFiles(testFiles); + await waitForTabs(testFiles); + + // Verify initial tab order + const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(initialWorkingSet.length).toBe(testFiles.length); + expect(initialWorkingSet[0].fullPath).toBe(testFiles[0]); + expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); + expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); + + // Simulate drag and drop from first tab to last tab + await simulateTabDragAndDrop(testFiles[0], testFiles[2], true); + + // Wait for the working set to update + await awaitsFor( + function () { + const currentWorkingSet = MainViewManager.getWorkingSet("first-pane"); + // Check if the first file has moved to before the last file + return ( + currentWorkingSet.length === testFiles.length && + currentWorkingSet[1].fullPath === testFiles[0] + ); + }, + "Working set to update after drag and drop", + 1000 + ); + + // Verify the new tab order + const finalWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(finalWorkingSet.length).toBe(testFiles.length); + expect(finalWorkingSet[0].fullPath).toBe(testFiles[1]); + expect(finalWorkingSet[1].fullPath).toBe(testFiles[0]); + expect(finalWorkingSet[2].fullPath).toBe(testFiles[2]); + + // Clean up - close all the test files + await closeTestFiles(testFiles); + }); + + it("should allow dragging and dropping a tab in between other tabs", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Create and open multiple test files to work with + const testFiles = await createTestFiles(3, "drag-between-test", "// Drag between test file"); + await openTestFiles(testFiles); + await waitForTabs(testFiles); + + // Verify initial tab order + const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(initialWorkingSet.length).toBe(testFiles.length); + expect(initialWorkingSet[0].fullPath).toBe(testFiles[0]); + expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); + expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); + + // Simulate drag and drop from last tab to before the middle tab + await simulateTabDragAndDrop(testFiles[2], testFiles[1], true); + + // Wait for the working set to update + await awaitsFor( + function () { + const currentWorkingSet = MainViewManager.getWorkingSet("first-pane"); + // Check if the last file has moved to between the first and second files + return ( + currentWorkingSet.length === testFiles.length && + currentWorkingSet[1].fullPath === testFiles[2] + ); + }, + "Working set to update after drag and drop", + 1000 + ); + + // Verify the new tab order + const finalWorkingSet = MainViewManager.getWorkingSet("first-pane"); + expect(finalWorkingSet.length).toBe(testFiles.length); + expect(finalWorkingSet[0].fullPath).toBe(testFiles[0]); + expect(finalWorkingSet[1].fullPath).toBe(testFiles[2]); // Last tab should now be in the middle + expect(finalWorkingSet[2].fullPath).toBe(testFiles[1]); // Middle tab should now be last + + // Clean up - close all the test files + await closeTestFiles(testFiles); + }); + + it("should allow dragging a tab from one pane to another non-empty pane", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Set up a horizontal split view (two columns) + MainViewManager.setLayoutScheme(1, 2); + + // Create test files for both panes + const firstPaneFiles = await createTestFiles(2, "first-pane-test", "// First pane test file"); + const secondPaneFiles = await createTestFiles(2, "second-pane-test", "// Second pane test file"); + + // Open files in both panes + await openTestFiles(firstPaneFiles, "first-pane"); + await openTestFiles(secondPaneFiles, "second-pane"); + + // Wait for all tabs to appear in both panes + await waitForTabs(firstPaneFiles, "first-pane"); + await waitForTabs(secondPaneFiles, "second-pane"); + + // Verify initial tab counts + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); + expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length); + + // Get the source tab from the first pane and target tab from the second pane + const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); + const targetTab = $(`.tab[data-path="${secondPaneFiles[0]}"]`); + + // Simulate drag start on the source tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the target tab + const dragEnterEvent = $.Event("dragenter"); + targetTab.trigger(dragEnterEvent); + + // Simulate drag over on the target tab + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + }, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {} + }); + targetTab.trigger(dragOverEvent); + + // Simulate drop on the target tab + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {}, + clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + targetTab.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + + // Wait for the tab to move to the second pane + await awaitsFor( + function () { + return ( + !tabExistsInPane(firstPaneFiles[0], "first-pane") && + tabExistsInPane(firstPaneFiles[0], "second-pane") + ); + }, + "Tab to move from first pane to second pane", + 1000 + ); + + // Verify the tab counts after the drag and drop + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); + expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length + 1); + + // Verify the tab is now in the second pane + expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); + expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); + + // Clean up - close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + }); + + it("should allow dragging a tab to an empty pane", async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Set up a horizontal split view (two columns) + MainViewManager.setLayoutScheme(1, 2); + + // Wait for layout to settle and ensure second pane is empty + await awaitsFor( + function () { + return MainViewManager.getPaneCount() === 2; + }, + "Layout to settle with two panes", + 1000 + ); + + // Force close any files that might be open in the second pane + const secondPaneWorkingSet = MainViewManager.getWorkingSet("second-pane"); + for (const file of secondPaneWorkingSet) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: file, paneId: "second-pane" }), + `Force close file ${file.fullPath} in second pane` + ); + } + + // Wait for the second pane to actually be empty after cleanup + await awaitsFor( + function () { + return getPaneTabCount("second-pane") === 0; + }, + "Second pane to be cleaned up", + 2000 + ); + + expect(getPaneTabCount("second-pane")).toBe(0); + + // Create test files for the first pane + const firstPaneFiles = await createTestFiles( + 2, + "first-pane-empty-test", + "// First pane empty test file" + ); + + // Open files in the first pane only + await openTestFiles(firstPaneFiles, "first-pane"); + + // Wait for all tabs to appear in the first pane + await waitForTabs(firstPaneFiles, "first-pane"); + + expect(getPaneTabCount("second-pane")).toBe(0); + + // Ensure second pane is empty before proceeding + await awaitsFor( + function () { + return getPaneTabCount("second-pane") === 0 && !isTabBarVisible("second-pane"); + }, + "Second pane to be empty", + 1000 + ); + + // Verify initial tab counts + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); + expect(getPaneTabCount("second-pane")).toBe(0); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Get the source tab from the first pane + const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); + + // Get the empty pane content area as the drop target + const emptyPaneTarget = $("#second-pane .pane-content"); + + // Simulate drag start on the source tab + const dragStartEvent = $.Event("dragstart", { + originalEvent: { + dataTransfer: { + setData: function () {}, + effectAllowed: "move" + } + } + }); + sourceTab.trigger(dragStartEvent); + + // Simulate dragenter on the empty pane + const dragEnterEvent = $.Event("dragenter"); + emptyPaneTarget.trigger(dragEnterEvent); + + // Simulate drag over on the empty pane + const dragOverEvent = $.Event("dragover", { + originalEvent: { + dataTransfer: { + dropEffect: "move" + } + }, + preventDefault: function () {} + }); + emptyPaneTarget.trigger(dragOverEvent); + + // Simulate drop on the empty pane + const dropEvent = $.Event("drop", { + originalEvent: { + dataTransfer: {} + }, + preventDefault: function () {}, + stopPropagation: function () {} + }); + emptyPaneTarget.trigger(dropEvent); + + // Simulate dragend to complete the operation + const dragEndEvent = $.Event("dragend"); + sourceTab.trigger(dragEndEvent); + + // Wait for the tab to move to the second pane + await awaitsFor( + function () { + return ( + !tabExistsInPane(firstPaneFiles[0], "first-pane") && + tabExistsInPane(firstPaneFiles[0], "second-pane") && + isTabBarVisible("second-pane") + ); + }, + "Tab to move from first pane to second pane and tab bar to appear", + 1000 + ); + + // Verify the tab counts after the drag and drop + expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); + expect(getPaneTabCount("second-pane")).toBe(1); + + // Verify the tab is now in the second pane + expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); + expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); + + // Clean up - close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + }); + }); + + describe("Working Set", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Wait for the tab bar to update + await awaitsFor( + function () { + return $("#phoenix-tab-bar").length > 0 && getTabCount() === 0; + }, + "Tab bar to update with no tabs", + 1000 + ); + }); + + it("should add tabs when files are added to the working set", async function () { + // Open the first test file and wait for its tab to appear + await openTestFiles([testFilePath]); + await waitForTabs([testFilePath]); + + // Verify the tab exists + expect(tabExists(testFilePath)).toBe(true); + expect(getTabCount()).toBe(1); + + // Open the second test file and wait for its tab to appear + await openTestFiles([testFilePath2]); + await waitForTabs([testFilePath2]); + + // Verify both tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(getTabCount()).toBe(2); + + // Open the third test file and wait for its tab to appear + await openTestFiles([testFilePath3]); + await waitForTabs([testFilePath3]); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(3); + }); + + it("should remove tabs when files are removed from the working set", async function () { + // Open all three test files + const testFiles = [testFilePath, testFilePath2, testFilePath3]; + await openTestFiles(testFiles); + await waitForTabs(testFiles); + + // Verify all three tabs exist + expect(getTabCount()).toBe(3); + + // Close the second test file + await closeTestFiles([testFilePath2]); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath2); + }, + "Tab for second file to disappear", + 1000 + ); + + // Verify the second tab is removed + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(2); + + // Close the first test file + await closeTestFiles([testFilePath]); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath); + }, + "Tab for first file to disappear", + 1000 + ); + + // Verify the first tab is removed + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(1); + + // Close the third test file + await closeTestFiles([testFilePath3]); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath3); + }, + "Tab for third file to disappear", + 1000 + ); + + // Verify all tabs are removed + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(0); + }); + }); + + describe("Active Tab", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Open all three test files + const testFiles = [testFilePath, testFilePath2, testFilePath3]; + await openTestFiles(testFiles); + await waitForTabs(testFiles); + }); + + it("should change active tab when switching files in the working set", async function () { + // Helper function to switch to a file and verify it's active + async function switchToFileAndVerify(filePath, description) { + // Switch to the file + await openTestFiles([filePath]); + + // Wait for the tab to become active + await awaitsFor( + function () { + return isTabActive(filePath); + }, + `${description} to become active`, + 1000 + ); + + // Verify this tab is active and others are not + expect(isTabActive(testFilePath)).toBe(filePath === testFilePath); + expect(isTabActive(testFilePath2)).toBe(filePath === testFilePath2); + expect(isTabActive(testFilePath3)).toBe(filePath === testFilePath3); + } + + // Test switching to each file + await switchToFileAndVerify(testFilePath, "First tab"); + await switchToFileAndVerify(testFilePath2, "Second tab"); + await switchToFileAndVerify(testFilePath3, "Third tab"); + }); + + it("should display active tab correctly based on the active file in the working set", async function () { + // Get the currently active file + const activeFile = MainViewManager.getCurrentlyViewedFile(); + + // Wait for tab bar to be recreated and reflect the active file + await awaitsFor( + function () { + return $("#phoenix-tab-bar").length > 0 && activeFile && isTabActive(activeFile.fullPath); + }, + "Tab bar to be recreated and show active file", + 1000 + ); + + // Verify the tab for the active file is active + expect(isTabActive(activeFile.fullPath)).toBe(true); + + // Switch to a different file + await openTestFiles([testFilePath2]); + + // Get the new active file + const newActiveFile = MainViewManager.getCurrentlyViewedFile(); + + // Wait for tab bar to update with the new active file + await awaitsFor( + function () { + return newActiveFile && isTabActive(newActiveFile.fullPath); + }, + "Tab bar to update with new active file", + 1000 + ); + + // Verify the tab for the new active file is active + expect(isTabActive(newActiveFile.fullPath)).toBe(true); + + // Verify the tab for the previous active file is no longer active + expect(isTabActive(activeFile.fullPath)).toBe(false); + }); + + it("should switch files properly when different tabs are clicked", async function () { + // Helper function to click a tab and verify it becomes active + async function clickTabAndVerify(filePath, description) { + const $tab = getTab(filePath); + expect($tab.length).toBe(1); + $tab.trigger("mousedown"); + + // Wait for the file to become active + await awaitsFor( + function () { + return ( + isTabActive(filePath) && MainViewManager.getCurrentlyViewedFile().fullPath === filePath + ); + }, + `${description} to become active after tab click`, + 1000 + ); + + // Verify this tab is active and others are not + const allPaths = [testFilePath, testFilePath2, testFilePath3]; + allPaths.forEach((path) => { + expect(isTabActive(path)).toBe(path === filePath); + }); + + // Verify the correct file is loaded in the editor + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(filePath); + } + // Wait for the tab bar to be properly loaded + await awaitsFor( + function () { + return $("#phoenix-tab-bar").length > 0 && getTabCount() === 3; + }, + "Tab bar to be properly loaded with all tabs", + 1000 + ); + + // Wait for the third file to become active + await awaitsFor( + function () { + return isTabActive(testFilePath3) && + MainViewManager.getCurrentlyViewedFile() && + MainViewManager.getCurrentlyViewedFile().fullPath === testFilePath3; + }, + "Third file to become active", + 2000 + ); + + // Initially, verify the third file is active (last opened) + expect(isTabActive(testFilePath3)).toBe(true); + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testFilePath3); + + // Test clicking on each tab + await clickTabAndVerify(testFilePath, "First file"); + await clickTabAndVerify(testFilePath2, "Second file"); + await clickTabAndVerify(testFilePath3, "Third file"); + + // Click back on the first tab to ensure it still works + await clickTabAndVerify(testFilePath, "First file"); + }); + }); + + describe("Overflow", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + }); + + it("should show overflow button when there are too many tabs to fit", async function () { + // Create several test files to ensure overflow + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); + + // Open all the test files + await openTestFiles(testFiles); + + // Wait for all tabs to appear + await waitForTabs(testFiles); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Verify the overflow button is visible + expect(isOverflowButtonVisible()).toBe(true); + + // Verify that some tabs are not visible + let visibleTabs = 0; + let hiddenTabs = 0; + for (const filePath of testFiles) { + if (isTabVisible(filePath)) { + visibleTabs++; + } else { + hiddenTabs++; + } + } + + // There should be at least one hidden tab + expect(hiddenTabs).toBeGreaterThan(0); + expect(visibleTabs + hiddenTabs).toBe(testFiles.length); + + // Clean up - close all the test files + await closeTestFiles(testFiles); + }); + + it("should display dropdown with hidden tabs when overflow button is clicked", async function () { + // Create several test files to ensure overflow + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); + + // Open all the test files + await openTestFiles(testFiles); + + // Wait for all tabs to appear + await waitForTabs(testFiles); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Get the list of hidden tabs + const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); + expect(hiddenFiles.length).toBeGreaterThan(0); + + // Click the overflow button + getOverflowButton().click(); + + // Wait for the dropdown to appear + await awaitsFor( + function () { + return getOverflowDropdown().length > 0; + }, + "Overflow dropdown to appear", + 1000 + ); + + // Verify the dropdown is visible + expect(getOverflowDropdown().length).toBeGreaterThan(0); + + // Verify the dropdown contains items for all hidden tabs + const dropdownItems = getOverflowDropdownItems(); + expect(dropdownItems.length).toBe(hiddenFiles.length); + + // Verify each hidden file has an item in the dropdown + for (const filePath of hiddenFiles) { + const item = getOverflowDropdownItem(filePath); + expect(item.length).toBe(1); + } + + // Clean up - close the dropdown by clicking elsewhere + $("body").click(); + + // Wait for the dropdown to disappear + await awaitsFor( + function () { + return getOverflowDropdown().length === 0; + }, + "Overflow dropdown to disappear", + 1000 + ); + + // Clean up - close all the test files + await closeTestFiles(testFiles); + }); + + it("should make tab visible and file active when clicking on item in overflow dropdown", async function () { + // Create several test files to ensure overflow + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); + + // Open all the test files + await openTestFiles(testFiles); + + // Wait for all tabs to appear + await waitForTabs(testFiles); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Get the list of hidden tabs + const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); + expect(hiddenFiles.length).toBeGreaterThan(0); + + // Select a hidden file to test + const testHiddenFile = hiddenFiles[0]; + + // Click the overflow button + getOverflowButton().click(); + + // Wait for the dropdown to appear + await awaitsFor( + function () { + return getOverflowDropdown().length > 0; + }, + "Overflow dropdown to appear", + 1000 + ); + + // Get the dropdown item for the test file + const dropdownItem = getOverflowDropdownItem(testHiddenFile); + expect(dropdownItem.length).toBe(1); + + // Click the dropdown item + dropdownItem.click(); + + // Wait for the file to become active + await awaitsFor( + function () { + return ( + isTabActive(testHiddenFile) && + MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile + ); + }, + "Hidden file to become active after dropdown item click", + 1000 + ); + + // Verify the file is active + expect(isTabActive(testHiddenFile)).toBe(true); + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testHiddenFile); + + // Verify the tab is now visible (scrolled into view) + await awaitsFor( + function () { + return isTabVisible(testHiddenFile); + }, + "Tab to become visible after dropdown item click", + 1000 + ); + + expect(isTabVisible(testHiddenFile)).toBe(true); + + // Clean up - close all the test files + await closeTestFiles(testFiles); + }); + + it("should scroll tab bar to make selected file visible when selecting from working set", async function () { + // Create several test files to ensure overflow + const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); + + // Open all the test files + await openTestFiles(testFiles); + + // Wait for all tabs to appear + await waitForTabs(testFiles); + + // Wait for the overflow button to appear + await awaitsFor( + function () { + return isOverflowButtonVisible(); + }, + "Overflow button to appear", + 1000 + ); + + // Get the list of hidden tabs + const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); + expect(hiddenFiles.length).toBeGreaterThan(0); + + // Select a hidden file to test + const testHiddenFile = hiddenFiles[0]; + + // Verify the tab is not visible initially + expect(isTabVisible(testHiddenFile)).toBe(false); + + // Select the file directly from the working set (not using the overflow dropdown) + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testHiddenFile }), + "Open hidden file from working set" + ); + + // Wait for the file to become active + await awaitsFor( + function () { + return ( + isTabActive(testHiddenFile) && + MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile + ); + }, + "Hidden file to become active after selection from working set", + 1000 + ); + + // Verify the file is active + expect(isTabActive(testHiddenFile)).toBe(true); + expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testHiddenFile); + + // Verify the tab is now visible (scrolled into view) + await awaitsFor( + function () { + return isTabVisible(testHiddenFile); + }, + "Tab to become visible after selection from working set", + 1000 + ); + + expect(isTabVisible(testHiddenFile)).toBe(true); + + // Clean up - close all the test files + await closeTestFiles(testFiles); + }); + }); + + describe("Tab Items", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + }); + + it("should display the correct tab name", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Get the filename from the path + const fileName = testFilePath.split("/").pop(); + + // Verify the tab name is correct + expect(getTabName(testFilePath).text()).toBe(fileName); + }); + + it("should display a file icon", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Verify the tab has a file icon + expect(hasFileIcon(testFilePath)).toBe(true); + }); + + it("should display a dirty indicator when the file is modified and remove it when saved", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Initially, the file should not be dirty + expect(isTabDirty(testFilePath)).toBe(false); + + // Get the document and modify it + const doc = DocumentManager.getOpenDocumentForPath(testFilePath); + doc.setText("// Modified content"); + + // Wait for the dirty indicator to appear + await awaitsFor( + function () { + return isTabDirty(testFilePath); + }, + "Dirty indicator to appear", + 1000 + ); + + // Verify the tab has a dirty indicator + expect(isTabDirty(testFilePath)).toBe(true); + + // Save the file + await awaitsForDone(CommandManager.execute(Commands.FILE_SAVE, { doc: doc }), "Save file"); + + // Wait for the dirty indicator to disappear + await awaitsFor( + function () { + return !isTabDirty(testFilePath); + }, + "Dirty indicator to disappear", + 1000 + ); + + // Verify the tab no longer has a dirty indicator + expect(isTabDirty(testFilePath)).toBe(false); + + // Revert the changes for cleanup + doc.setText("// Test file 1 for TabBar"); + await awaitsForDone( + CommandManager.execute(Commands.FILE_SAVE, { doc: doc }), + "Save file with original content" + ); + }); + + it("should display directory name for files with the same name", async function () { + // Open both duplicate files + const duplicateFile1 = testDuplicateDir1 + "/" + testDuplicateName; + const duplicateFile2 = testDuplicateDir2 + "/" + testDuplicateName; + + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: duplicateFile1 }), + "Open first duplicate file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: duplicateFile2 }), + "Open second duplicate file" + ); + + // Wait for both tabs to appear + await awaitsFor( + function () { + return tabExists(duplicateFile1) && tabExists(duplicateFile2); + }, + "Both duplicate tabs to appear", + 1000 + ); + + // Verify both tabs have directory names + expect(hasDirectoryName(duplicateFile1)).toBe(true); + expect(hasDirectoryName(duplicateFile2)).toBe(true); + + // Verify the directory names are correct + expect(getDirectoryName(duplicateFile1)).toContain("dir1"); + expect(getDirectoryName(duplicateFile2)).toContain("dir2"); + }); + + it("should display the full file path in the tooltip", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Verify the tooltip contains the full path + const tooltip = getTabTooltip(testFilePath); + expect(tooltip).toContain(Phoenix.app.getDisplayPath(testFilePath)); + }); + + it("should display git change markers when git is enabled", async function () { + // Skip this test if Git integration is not available + if (!testWindow.brackets.test.Phoenix || !testWindow.brackets.test.Phoenix.app) { + expect("Test skipped - Phoenix.app not available").toBe("Test skipped - Phoenix.app not available"); + return; + } + + // Create a mock for the Git integration + if (!testWindow.phoenixGitEvents) { + testWindow.phoenixGitEvents = {}; + } + + // Save the original Git integration if it exists + const originalGitEvents = testWindow.phoenixGitEvents.TabBarIntegration; + + // Create a mock TabBarIntegration + testWindow.phoenixGitEvents.TabBarIntegration = { + isUntracked: function (path) { + return path === testFilePath; // Mark the first file as untracked + }, + isModified: function (path) { + return path === testFilePath2; // Mark the second file as modified + } + }; + + // Make sure the EventEmitter exists + if (!testWindow.phoenixGitEvents.EventEmitter) { + testWindow.phoenixGitEvents.EventEmitter = { + on: function () {}, + emit: function () {} + }; + } + + // Open the test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + + // Trigger a Git status update + testWindow.phoenixGitEvents.EventEmitter.emit("GIT_FILE_STATUS_CHANGED"); + + // Wait for the tabs to update with Git status + await awaitsFor( + function () { + return hasGitStatus(testFilePath) && hasGitStatus(testFilePath2); + }, + "Tabs to update with Git status", + 1000 + ); + + // Verify the first file has the git-new class + const $tab1 = getTab(testFilePath); + expect($tab1.hasClass("git-new")).toBe(true); + expect(hasGitStatus(testFilePath)).toBe(true); + + // Verify the second file has the git-modified class + const $tab2 = getTab(testFilePath2); + expect($tab2.hasClass("git-modified")).toBe(true); + expect(hasGitStatus(testFilePath2)).toBe(true); + + // Verify the tooltips contain the Git status + expect(getTabTooltip(testFilePath)).toContain("Untracked"); + expect(getTabTooltip(testFilePath2)).toContain("Modified"); + + // Restore the original Git integration + testWindow.phoenixGitEvents.TabBarIntegration = originalGitEvents; + }); + + it("should display a close button", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Verify the tab has a close button + expect(hasCloseButton(testFilePath)).toBe(true); + + // Verify the close button has the correct icon + const $closeButton = getTab(testFilePath).find(".tab-close"); + expect($closeButton.find("i.fa-times").length).toBe(1); + }); + + it("should close the file when the close button is clicked", async function () { + // Open the first test file + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open test file" + ); + + // Wait for the tab to appear + await awaitsFor( + function () { + return tabExists(testFilePath); + }, + "Tab to appear", + 1000 + ); + + // Get the close button + const $closeButton = getTab(testFilePath).find(".tab-close"); + + // Create a spy for the FILE_CLOSE command + const executeOriginal = CommandManager.execute; + let fileCloseCalled = false; + let fileClosePathArg = null; + + CommandManager.execute = function (command, args) { + if (command === Commands.FILE_CLOSE) { + fileCloseCalled = true; + if (args && args.file) { + fileClosePathArg = args.file.fullPath; + } + } + return executeOriginal.apply(CommandManager, arguments); + }; + + // Click the close button + $closeButton.click(); + + // Cancel the save dialog if it appears + cancelSaveDialog(); + + // Wait for the tab to disappear + await awaitsFor( + function () { + return !tabExists(testFilePath); + }, + "Tab to disappear", + 1000 + ); + + // Restore the original execute function + CommandManager.execute = executeOriginal; + + // Verify the FILE_CLOSE command was called with the correct file + expect(fileCloseCalled).toBe(true); + expect(fileClosePathArg).toBe(testFilePath); + }); + }); + + describe("Context Menu", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + }); + + /** + * Helper function to get the context menu element + * @returns {jQuery} - The context menu element + */ + function getContextMenu() { + return $(".tabbar-context-menu"); + } + + it("should open context menu when right-clicking on a tab", async function () { + // Get the tab element + const $tab = getTab(testFilePath); + expect($tab.length).toBe(1); + + // Simulate a right-click (contextmenu) event on the tab + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for the context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Verify the context menu is visible + expect(getContextMenu().length).toBe(1); + expect(getContextMenu().is(":visible")).toBe(true); + + // Clean up - close the context menu by clicking elsewhere + $("body").click(); + + // Wait for the context menu to disappear + await awaitsFor( + function () { + return getContextMenu().length === 0; + }, + "Context menu to disappear", + 1000 + ); + }); + + it("should close the tab when selecting 'Close Tab' from context menu", async function () { + // Get the tab element + const $tab = getTab(testFilePath); + + // Right-click on the tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close Tab" option + const $closeTabOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_TAB; + }); + expect($closeTabOption.length).toBe(1); + $closeTabOption.click(); + + // Cancel the save dialog if it appears + cancelSaveDialog(); + + // Verify the tab is closed + await awaitsFor( + function () { + return !tabExists(testFilePath); + }, + "Tab to be closed", + 1000 + ); + }); + + it("should close tabs to the right when selecting 'Close tabs to the right' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Get the first tab element + const $tab = getTab(testFilePath); + + // Right-click on the first tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close tabs to the right" option + const $closeTabsToRightOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_RIGHT; + }); + expect($closeTabsToRightOption.length).toBe(1); + $closeTabsToRightOption.click(); + + // Cancel any save dialogs that might appear + cancelSaveDialog(); + + // Verify tabs to the right are closed + await awaitsFor( + function () { + return tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); + }, + "Tabs to the right to be closed", + 1000 + ); + + // Verify only the first tab remains + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(1); + }); + + it("should close tabs to the left when selecting 'Close tabs to the left' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Get the third tab element + const $tab = getTab(testFilePath3); + + // Right-click on the third tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close tabs to the left" option + const $closeTabsToLeftOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_LEFT; + }); + expect($closeTabsToLeftOption.length).toBe(1); + $closeTabsToLeftOption.click(); + + // Cancel any save dialogs that might appear + cancelSaveDialog(); + + // Verify tabs to the left are closed + await awaitsFor( + function () { + return !tabExists(testFilePath) && !tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "Tabs to the left to be closed", + 1000 + ); + + // Verify only the third tab remains + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(true); + expect(getTabCount()).toBe(1); + }); + + it("should close saved tabs when selecting 'Close saved tabs' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Make the second file dirty + const doc2 = DocumentManager.getOpenDocumentForPath(testFilePath2); + doc2.setText("// Modified content"); + + // Wait for the dirty indicator to appear + await awaitsFor( + function () { + return isTabDirty(testFilePath2); + }, + "Dirty indicator to appear", + 1000 + ); + + // Verify the second tab is dirty + expect(isTabDirty(testFilePath2)).toBe(true); + + // Get any tab element (we'll use the first) + const $tab = getTab(testFilePath); + + // Right-click on the tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close saved tabs" option + const $closeSavedTabsOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_SAVED_TABS; + }); + expect($closeSavedTabsOption.length).toBe(1); + $closeSavedTabsOption.click(); + + // Cancel any save dialogs that might appear + cancelSaveDialog(); + + // Verify only the dirty tab remains + await awaitsFor( + function () { + return !tabExists(testFilePath) && tabExists(testFilePath2) && !tabExists(testFilePath3); + }, + "Saved tabs to be closed", + 1000 + ); + + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(1); + expect(isTabDirty(testFilePath2)).toBe(true); + + // Clean up - revert changes to the second file + doc2.setText("// Test file 2 for TabBar"); + await awaitsForDone( + CommandManager.execute(Commands.FILE_SAVE, { doc: doc2 }), + "Save file with original content" + ); + }); + + it("should close all tabs when selecting 'Close all tabs' from context menu", async function () { + // Open all three test files + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), + "Open first test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), + "Open second test file" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), + "Open third test file" + ); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); + }, + "All tabs to appear", + 1000 + ); + + // Verify all three tabs exist + expect(tabExists(testFilePath)).toBe(true); + expect(tabExists(testFilePath2)).toBe(true); + expect(tabExists(testFilePath3)).toBe(true); + + // Get any tab element (we'll use the first) + const $tab = getTab(testFilePath); + + // Right-click on the tab to open context menu + $tab.trigger("contextmenu", { + pageX: 100, + pageY: 100 + }); + + // Wait for context menu to appear + await awaitsFor( + function () { + return getContextMenu().length > 0; + }, + "Context menu to appear", + 1000 + ); + + // Find and click the "Close all tabs" option + const $closeAllTabsOption = getContextMenu() + .find("a.stylesheet-link") + .filter(function () { + return $(this).text().trim() === Strings.CLOSE_ALL_TABS; + }); + expect($closeAllTabsOption.length).toBe(1); + $closeAllTabsOption.click(); + + // Cancel any save dialogs that might appear + cancelSaveDialog(); + + // Verify all tabs are closed + await awaitsFor( + function () { + return !tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); + }, + "All tabs to be closed", + 1000 + ); + + expect(tabExists(testFilePath)).toBe(false); + expect(tabExists(testFilePath2)).toBe(false); + expect(tabExists(testFilePath3)).toBe(false); + expect(getTabCount()).toBe(0); + }); + }); + + describe("Number of Tabs Preference", function () { + beforeEach(async function () { + // Enable the tab bar feature with default settings + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + }); + + afterEach(async function () { + // Reset preferences to default + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + }); + + it("should show all tabs when numberOfTabs is set to -1", async function () { + // Create several test files + const testFiles = []; + for (let i = 0; i < 10; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for all tabs to appear + await awaitsFor( + function () { + return getTabCount() >= testFiles.length; + }, + "All tabs to appear", + 1000 + ); + + // Verify all tabs are shown + expect(getTabCount()).toBe(testFiles.length); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + cancelSaveDialog(); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should limit the number of tabs shown when numberOfTabs is set to a positive value", async function () { + // Set the preference to show only 5 tabs + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 5 }); + + // Create several test files + const testFiles = []; + for (let i = 0; i < 10; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for tabs to appear + await awaitsFor( + function () { + return getTabCount() > 0; + }, + "Tabs to appear", + 1000 + ); + + // Verify only 5 tabs are shown + expect(getTabCount()).toBe(5); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + cancelSaveDialog(); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should hide the tab bar when numberOfTabs is set to 0", async function () { + // First open some files with the default setting + const testFiles = []; + for (let i = 0; i < 3; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; + testFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); + } + + // Open all the test files + for (const filePath of testFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open file ${filePath}` + ); + } + + // Wait for tabs to appear + await awaitsFor( + function () { + return getTabCount() > 0; + }, + "Tabs to appear", + 1000 + ); + + // Verify tab bar is visible + expect($("#phoenix-tab-bar").is(":visible")).toBe(true); + + // Now set numberOfTabs to 0 + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 0 }); + + // Wait for tab bar to disappear + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible"); + }, + "Tab bar to disappear", + 1000 + ); + + // Verify tab bar is hidden + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + + // Clean up - close all the test files + for (const filePath of testFiles) { + const fileToClose = FileSystem.getFileForPath(filePath); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); + cancelSaveDialog(); + await awaitsForDone(promise, `Close file ${filePath}`); + } + }); + + it("should apply numberOfTabs preference to both panes", async function () { + // Set up split pane layout + MainViewManager.setLayoutScheme(1, 2); + + // Set the preference to show only 3 tabs + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 3 }); + + // Create test files for first pane + const firstPaneFiles = []; + for (let i = 0; i < 5; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-${i}.js`; + firstPaneFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// First pane file ${i}`, FileSystem)); + } + + // Create test files for second pane + const secondPaneFiles = []; + for (let i = 0; i < 5; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/second-pane-${i}.js`; + secondPaneFiles.push(filePath); + await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Second pane file ${i}`, FileSystem)); + } + + // Open files in first pane + for (const filePath of firstPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), + `Open file ${filePath} in first pane` + ); + } + + // Open files in second pane + for (const filePath of secondPaneFiles) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "second-pane" }), + `Open file ${filePath} in second pane` + ); + } + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return $("#phoenix-tab-bar").is(":visible") && $("#phoenix-tab-bar-2").is(":visible"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify each pane shows only 3 tabs + expect($("#phoenix-tab-bar").find(".tab").length).toBe(3); + expect($("#phoenix-tab-bar-2").find(".tab").length).toBe(3); + + // Change preference to show all tabs + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Wait for all tabs to appear + await awaitsFor( + function () { + return ( + $("#phoenix-tab-bar").find(".tab").length === 5 && + $("#phoenix-tab-bar-2").find(".tab").length === 5 + ); + }, + "All tabs to appear in both panes", + 1000 + ); + + // Verify all tabs are shown in both panes + expect($("#phoenix-tab-bar").find(".tab").length).toBe(5); + expect($("#phoenix-tab-bar-2").find(".tab").length).toBe(5); + + // Change preference to hide tab bars + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 0 }); + + // Wait for both tab bars to disappear + await awaitsFor( + function () { + return !$("#phoenix-tab-bar").is(":visible") && !$("#phoenix-tab-bar-2").is(":visible"); + }, + "Both tab bars to disappear", + 1000 + ); + + // Verify both tab bars are hidden + expect($("#phoenix-tab-bar").is(":visible")).toBe(false); + expect($("#phoenix-tab-bar-2").is(":visible")).toBe(false); + + // Clean up - close all files and reset to single pane + await testWindow.closeAllFiles(); + MainViewManager.setLayoutScheme(1, 1); + }); + }); + + describe("Split Panes", function () { + beforeEach(async function () { + // Enable the tab bar feature + PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); + + // Close all files to start with a clean state + await testWindow.closeAllFiles(); + + // Set up a horizontal split view (two columns) + MainViewManager.setLayoutScheme(1, 2); + }); + + afterEach(async function () { + // Reset to single pane layout + MainViewManager.setLayoutScheme(1, 1); + }); + + it("should show tab bars in both panes when files are open in both", async function () { + // Open a file in the first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Open a different file in the second pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), + "Open file in second pane" + ); + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify both tab bars are visible + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Verify each pane has the correct tab + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(1); + expect(getPaneTabCount("second-pane")).toBe(1); + }); + + it("should hide tab bar in a pane with no files", async function () { + // Open a file in the first pane only + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Wait for the first tab bar to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane"); + }, + "First tab bar to appear", + 1000 + ); + + // Verify first tab bar is visible and second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Verify the first pane has the correct tab + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(1); + }); + + it("should update tab bars when moving files between panes", async function () { + // Open a file in the first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Wait for the first tab bar to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane"); + }, + "First tab bar to appear", + 1000 + ); + + // Verify first tab bar is visible and second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Move the file to the second pane + const fileObj = FileSystem.getFileForPath(testFilePath); + MainViewManager.addToWorkingSet("second-pane", fileObj); + + // Remove from first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: "first-pane" }), + "Close file in first pane" + ); + + // Wait for the tab to appear in the second pane and disappear from the first + await awaitsFor( + function () { + return ( + !tabExistsInPane(testFilePath, "first-pane") && tabExistsInPane(testFilePath, "second-pane") + ); + }, + "Tab to move to second pane", + 1000 + ); + + // Verify the tab bars visibility has updated + expect(isTabBarVisible("first-pane")).toBe(false); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Verify the tab is now in the second pane + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(false); + expect(tabExistsInPane(testFilePath, "second-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(0); + expect(getPaneTabCount("second-pane")).toBe(1); + }); + + it("should hide tab bar when closing all files in a pane", async function () { + // Open files in both panes + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), + "Open file in second pane" + ); + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify both tab bars are visible + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Close the file in the second pane + const fileToClose = FileSystem.getFileForPath(testFilePath2); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { + file: fileToClose, + paneId: "second-pane" + }); + + // Cancel the save dialog if it appears + cancelSaveDialog(); + + await awaitsForDone(promise, "Close file in second pane"); + + // Wait for the second tab bar to disappear + await awaitsFor( + function () { + return !isTabBarVisible("second-pane"); + }, + "Second tab bar to disappear", + 1000 + ); + + // Verify first tab bar is still visible but second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Verify the tabs are in the correct panes + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(false); + expect(getPaneTabCount("first-pane")).toBe(1); + expect(getPaneTabCount("second-pane")).toBe(0); + }); + + it("should work correctly with vertical split layout", async function () { + // Change to vertical split layout (2 rows, 1 column) + MainViewManager.setLayoutScheme(2, 1); + + // Open a file in the first pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), + "Open file in first pane" + ); + + // Open a different file in the second pane + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), + "Open file in second pane" + ); + + // Wait for both tab bars to appear + await awaitsFor( + function () { + return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); + }, + "Both tab bars to appear", + 1000 + ); + + // Verify both tab bars are visible + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(true); + + // Verify each pane has the correct tab + expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); + expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(true); + expect(getPaneTabCount("first-pane")).toBe(1); + expect(getPaneTabCount("second-pane")).toBe(1); + + // Close the file in the second pane + const fileToClose = FileSystem.getFileForPath(testFilePath2); + const promise = CommandManager.execute(Commands.FILE_CLOSE, { + file: fileToClose, + paneId: "second-pane" + }); + + // Cancel the save dialog if it appears + cancelSaveDialog(); + + await awaitsForDone(promise, "Close file in second pane"); + + // Wait for the second tab bar to disappear + await awaitsFor( + function () { + return !isTabBarVisible("second-pane"); + }, + "Second tab bar to disappear", + 1000 + ); + + // Verify first tab bar is still visible but second is not + expect(isTabBarVisible("first-pane")).toBe(true); + expect(isTabBarVisible("second-pane")).toBe(false); + + // Reset to horizontal split for other tests + MainViewManager.setLayoutScheme(1, 2); + }); + }); + + describe("Tab Bar Scrolling", function () { + let longTestFilePaths = []; + + beforeEach(async function () { + // Create multiple test files to ensure scrolling is needed + longTestFilePaths = []; + for (let i = 1; i <= 15; i++) { + const filePath = SpecRunnerUtils.getTempDirectory() + `/scroll-test-file-${i}.js`; + longTestFilePaths.push(filePath); + await jsPromise( + SpecRunnerUtils.createTextFile(filePath, `// Test file ${i} for scrolling`, FileSystem) + ); + } + + // Open all files to create many tabs + for (let filePath of longTestFilePaths) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), + `Open ${filePath}` + ); + } + + // Wait for tabs to be rendered + await awaitsFor( + function () { + return getTabCount() >= 15; + }, + "All tabs to be created", + 3000 + ); + }); + + afterEach(async function () { + // Close all test files + for (let filePath of longTestFilePaths) { + const fileObj = FileSystem.getFileForPath(filePath); + try { + await awaitsForDone( + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj }), + `Close ${filePath}` + ); + } catch (e) { + // Ignore errors if file is already closed + } + } + }); + + it("should scroll tab bar horizontally when mouse wheel is scrolled", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Get initial scroll position + const initialScrollLeft = $tabBar.scrollLeft(); + + // Create a wheel event for scrolling down (should scroll right) + const wheelEventDown = $.Event("wheel"); + wheelEventDown.originalEvent = { deltaY: 100 }; // Positive deltaY = scroll down/right + + // Trigger the wheel event + $tabBar.trigger(wheelEventDown); + + // Check that scroll position has changed to the right + const scrollAfterDown = $tabBar.scrollLeft(); + expect(scrollAfterDown).toBeGreaterThan(initialScrollLeft); + // Verify the scroll amount is proportional to deltaY (implementation multiplies by 2.5) + expect(scrollAfterDown - initialScrollLeft).toBeCloseTo(100 * 2.5, 0); + + // Create a wheel event for scrolling up (should scroll left) + const wheelEventUp = $.Event("wheel"); + wheelEventUp.originalEvent = { deltaY: -100 }; // Negative deltaY = scroll up/left + + // Trigger the wheel event + $tabBar.trigger(wheelEventUp); + + // Check that scroll position has moved left from the previous position + const scrollAfterUp = $tabBar.scrollLeft(); + expect(scrollAfterUp).toBeLessThan(scrollAfterDown); + // Verify the scroll amount is proportional to deltaY + expect(scrollAfterDown - scrollAfterUp).toBeCloseTo(100 * 2.5, 0); + }); + + it("should scroll tab bar with trackpad scrolling", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Get initial scroll position + const initialScrollLeft = $tabBar.scrollLeft(); + + // Create a wheel event simulating trackpad scrolling (smaller deltaY values) + const trackpadEvent = $.Event("wheel"); + trackpadEvent.originalEvent = { deltaY: 25 }; // Smaller value typical of trackpad + + // Trigger the trackpad scrolling event multiple times + for (let i = 0; i < 4; i++) { + $tabBar.trigger(trackpadEvent); + } + + // Check that scroll position has changed + const scrollAfterTrackpad = $tabBar.scrollLeft(); + expect(scrollAfterTrackpad).toBeGreaterThan(initialScrollLeft); + // Verify the total scroll amount after multiple small scrolls + // 4 scrolls of 25 * 2.5 = 250 pixels total + expect(scrollAfterTrackpad - initialScrollLeft).toBeCloseTo(4 * 25 * 2.5, 0); + }); + + it("should scroll second pane tab bar when it exists", async function () { + // Create the second pane first + MainViewManager.setLayoutScheme(1, 2); + + // Wait for the layout to be created + await awaitsFor( + function () { + return MainViewManager.getPaneIdList().length === 2; + }, + "Second pane to be created", + 1000 + ); + + // Open a file in the second pane to create the second tab bar + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { + fullPath: longTestFilePaths[0], + paneId: "second-pane" + }), + "Open file in second pane" + ); + + // Wait for second tab bar to appear + await awaitsFor( + function () { + return $("#phoenix-tab-bar-2").length > 0; + }, + "Second tab bar to appear", + 1000 + ); + + const $tabBar2 = $("#phoenix-tab-bar-2"); + expect($tabBar2.length).toBe(1); + + // Open multiple files in second pane to enable scrolling + for (let i = 1; i < 8; i++) { + await awaitsForDone( + CommandManager.execute(Commands.FILE_OPEN, { + fullPath: longTestFilePaths[i], + paneId: "second-pane" + }), + `Open file ${i} in second pane` + ); + } + + // Wait for tabs to be rendered in second pane + await awaitsFor( + function () { + return $tabBar2.find(".tab").length >= 8; + }, + "Tabs to be created in second pane", + 2000 + ); + + // Get initial scroll position of second tab bar + const initialScrollLeft = $tabBar2.scrollLeft(); + + // Create a wheel event for scrolling + const wheelEvent = $.Event("wheel"); + wheelEvent.originalEvent = { deltaY: 150 }; + + // Trigger the wheel event on second tab bar + $tabBar2.trigger(wheelEvent); + + // Check that scroll position has changed + const scrollAfterWheel = $tabBar2.scrollLeft(); + expect(scrollAfterWheel).toBeGreaterThan(initialScrollLeft); + // Verify the scroll amount is proportional to deltaY + expect(scrollAfterWheel - initialScrollLeft).toBeCloseTo(150 * 2.5, 0); + + // Reset layout scheme back to single pane + MainViewManager.setLayoutScheme(1, 1); + }); + + it("should calculate correct scroll amount based on deltaY", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Ensure the tab bar is scrollable by checking if scrollWidth > clientWidth + if ($tabBar[0].scrollWidth <= $tabBar[0].clientWidth) { + // Skip test if tab bar is not scrollable + return; + } + + // Set initial scroll position to middle to allow scrolling in both directions + const maxScroll = $tabBar[0].scrollWidth - $tabBar[0].clientWidth; + const midScroll = Math.floor(maxScroll / 2); + $tabBar.scrollLeft(midScroll); + + // Test positive deltaY (scroll right) + const initialScrollLeft = $tabBar.scrollLeft(); + const wheelEventRight = $.Event("wheel"); + wheelEventRight.originalEvent = { deltaY: 40 }; + $tabBar.trigger(wheelEventRight); + + const scrollAfterRight = $tabBar.scrollLeft(); + expect(scrollAfterRight).toBeGreaterThan(initialScrollLeft); + expect(scrollAfterRight - initialScrollLeft).toBeCloseTo(40 * 2.5, 0); + + // Reset and test negative deltaY (scroll left) + $tabBar.scrollLeft(midScroll); + const wheelEventLeft = $.Event("wheel"); + wheelEventLeft.originalEvent = { deltaY: -40 }; + $tabBar.trigger(wheelEventLeft); + + const scrollAfterLeft = $tabBar.scrollLeft(); + expect(scrollAfterLeft).toBeLessThan(midScroll); + expect(midScroll - scrollAfterLeft).toBeCloseTo(40 * 2.5, 0); + }); + + it("should not scroll beyond the scrollable bounds", function () { + const $tabBar = $("#phoenix-tab-bar"); + expect($tabBar.length).toBe(1); + + // Get the maximum scrollable width + const maxScrollLeft = $tabBar[0].scrollWidth - $tabBar[0].clientWidth; + + // Scroll far to the right + $tabBar.scrollLeft(maxScrollLeft + 1000); // Try to scroll beyond max + + // Create a wheel event to scroll further right + const wheelEventRight = $.Event("wheel"); + wheelEventRight.originalEvent = { deltaY: 500 }; // Large scroll right + $tabBar.trigger(wheelEventRight); + + // Should not exceed maximum scroll (a small floating point tolerance) + expect($tabBar.scrollLeft()).toBeLessThanOrEqual(maxScrollLeft + 1); + + // Scroll far to the left + $tabBar.scrollLeft(-1000); // Try to scroll beyond minimum + + // Create a wheel event to scroll further left + const wheelEventLeft = $.Event("wheel"); + wheelEventLeft.originalEvent = { deltaY: -500 }; // Large scroll left + $tabBar.trigger(wheelEventLeft); + + // Should not go below 0 (a small floating point tolerance) + expect($tabBar.scrollLeft()).toBeGreaterThanOrEqual(-1); + }); + }); + }); +}); From 6993085c0e39760c4b534fc0e80ec03d1ea4ae42 Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 15:23:59 +0530 Subject: [PATCH 23/26] fix: remove redundant scroll calculation --- test/spec/Extn-Tabbar-integ-test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index f142f78c95..4d76193ec4 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -2918,9 +2918,6 @@ define(function (require, exports, module) { // Check that scroll position has changed const scrollAfterTrackpad = $tabBar.scrollLeft(); expect(scrollAfterTrackpad).toBeGreaterThan(initialScrollLeft); - // Verify the total scroll amount after multiple small scrolls - // 4 scrolls of 25 * 2.5 = 250 pixels total - expect(scrollAfterTrackpad - initialScrollLeft).toBeCloseTo(4 * 25 * 2.5, 0); }); it("should scroll second pane tab bar when it exists", async function () { From d4eadcbefff262678f3543983faf3701f09a8fc0 Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 16:03:27 +0530 Subject: [PATCH 24/26] fix: delete redundant file --- test/spec/Extn-Tabbar-integ-test.js~ | 3063 -------------------------- 1 file changed, 3063 deletions(-) delete mode 100644 test/spec/Extn-Tabbar-integ-test.js~ diff --git a/test/spec/Extn-Tabbar-integ-test.js~ b/test/spec/Extn-Tabbar-integ-test.js~ deleted file mode 100644 index 0a9bc045c9..0000000000 --- a/test/spec/Extn-Tabbar-integ-test.js~ +++ /dev/null @@ -1,3063 +0,0 @@ -/* - * GNU AGPL-3.0 License - * - * Copyright (c) 2021 - present core.ai . All rights reserved. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License - * for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0. - * - */ - -/*global describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, awaitsFor, awaitsForDone, awaits, jsPromise */ - -define(function (require, exports, module) { - const SpecRunnerUtils = require("spec/SpecRunnerUtils"); - - describe("integration:TabBar", function () { - let testWindow, - PreferencesManager, - $, - FileSystem, - MainViewManager, - CommandManager, - Commands, - DocumentManager, - Strings; - let testFilePath, testFilePath2, testFilePath3, testDuplicateDir1, testDuplicateDir2, testDuplicateName; - - /** - * Helper function to create multiple test files - * @param {number} count - Number of files to create - * @param {string} prefix - Prefix for the file names - * @param {string} content - Content template for the files (will be appended with file index) - * @returns {Promise} - Array of file paths - */ - async function createTestFiles(count, prefix, content) { - const testFiles = []; - for (let i = 0; i < count; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/${prefix}-${i}.js`; - testFiles.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile( - filePath, - content ? `${content} ${i}` : `// ${prefix} test file ${i}`, - FileSystem - ) - ); - } - return testFiles; - } - - /** - * Helper function to open multiple files - * @param {string[]} filePaths - Array of file paths to open - * @param {string} [paneId] - Optional pane ID to open the files in - * @returns {Promise} - */ - async function openTestFiles(filePaths, paneId) { - for (const filePath of filePaths) { - const options = { fullPath: filePath }; - if (paneId) { - options.paneId = paneId; - } - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, options), - `Open file ${filePath}${paneId ? ` in ${paneId}` : ""}` - ); - } - } - - /** - * Helper function to wait for tabs to appear - * @param {string[]} filePaths - Array of file paths to wait for - * @param {string} [paneId] - Optional pane ID to check for tabs - * @returns {Promise} - */ - async function waitForTabs(filePaths, paneId) { - if (paneId) { - // Wait for tabs to appear in the specified pane - await awaitsFor( - function () { - return getPaneTabCount(paneId) >= filePaths.length; - }, - `All tabs to appear in ${paneId}`, - 1000 - ); - } else if (filePaths.length === 1) { - // Wait for a single tab to appear - await awaitsFor( - function () { - return tabExists(filePaths[0]); - }, - `Tab for ${filePaths[0]} to appear`, - 1000 - ); - } else { - // Wait for multiple tabs to appear - await awaitsFor( - function () { - return getTabCount() >= filePaths.length && filePaths.every((path) => tabExists(path)); - }, - "All tabs to appear", - 1000 - ); - } - } - - /** - * Helper function to cancel the save dialog - * @returns {void} - */ - function cancelSaveDialog() { - testWindow.brackets.test.Dialogs.cancelModalDialogIfOpen( - testWindow.brackets.test.DefaultDialogs.DIALOG_ID_SAVE_CLOSE, - testWindow.brackets.test.DefaultDialogs.DIALOG_BTN_DONTSAVE - ); - } - - /** - * Helper function to close files - * @param {string[]} filePaths - Array of file paths to close - * @param {string} [paneId] - Optional pane ID to close the files from - * @returns {Promise} - */ - async function closeTestFiles(filePaths, paneId) { - for (const filePath of filePaths) { - const fileToClose = FileSystem.getFileForPath(filePath); - const options = { file: fileToClose }; - if (paneId) { - options.paneId = paneId; - } - const promise = CommandManager.execute(Commands.FILE_CLOSE, options); - cancelSaveDialog(); - await awaitsForDone(promise, `Close file ${filePath}`); - } - } - - /** - * Helper function to simulate drag and drop between tabs - * @param {string} sourceFilePath - Path of the source file to drag - * @param {string} targetFilePath - Path of the target file to drop onto - * @param {boolean} [dropBefore=true] - Whether to drop before the target (true) or after (false) - * @returns {Promise} - */ - async function simulateTabDragAndDrop(sourceFilePath, targetFilePath, dropBefore = true) { - // Get the source and target tabs - const sourceTab = getTab(sourceFilePath); - const targetTab = getTab(targetFilePath); - - // Simulate drag start on the source tab - const dragStartEvent = $.Event("dragstart", { - originalEvent: { - dataTransfer: { - setData: function () {}, - effectAllowed: "move" - } - } - }); - sourceTab.trigger(dragStartEvent); - - // Simulate dragenter on the target tab - const dragEnterEvent = $.Event("dragenter"); - targetTab.trigger(dragEnterEvent); - - // Simulate drag over on the target tab - const targetRect = targetTab[0].getBoundingClientRect(); - const dropX = dropBefore - ? targetRect.left + 5 // Position near the left edge to drop before - : targetRect.right - 5; // Position near the right edge to drop after - - const dragOverEvent = $.Event("dragover", { - originalEvent: { - dataTransfer: { - dropEffect: "move" - }, - clientX: dropX - }, - preventDefault: function () {} - }); - targetTab.trigger(dragOverEvent); - - // Simulate drop on the target tab - const dropEvent = $.Event("drop", { - originalEvent: { - dataTransfer: {}, - clientX: dropX - }, - preventDefault: function () {}, - stopPropagation: function () {} - }); - targetTab.trigger(dropEvent); - - // Simulate dragend to complete the operation - const dragEndEvent = $.Event("dragend"); - sourceTab.trigger(dragEndEvent); - } - - /** - * Helper function to check if the tab bar for a specific pane is visible - * @param {string} paneId - The pane ID ("first-pane" or "second-pane") - * @returns {boolean} - True if the tab bar is visible, false otherwise - */ - function isTabBarVisible(paneId) { - const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; - return $(tabBarId).is(":visible"); - } - - /** - * Helper function to get the tab count for a specific pane - * @param {string} paneId - The pane ID ("first-pane" or "second-pane") - * @returns {number} - The number of tabs in the pane - */ - function getPaneTabCount(paneId) { - const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; - return $(tabBarId).find(".tab").length; - } - - /** - * Helper function to check if a tab for a specific file exists in a specific pane - * @param {string} filePath - The path of the file to check - * @param {string} paneId - The pane ID ("first-pane" or "second-pane") - * @returns {boolean} - True if the tab exists in the pane, false otherwise - */ - function tabExistsInPane(filePath, paneId) { - const tabBarId = paneId === "first-pane" ? "#phoenix-tab-bar" : "#phoenix-tab-bar-2"; - return $(tabBarId).find(`.tab[data-path="${filePath}"]`).length > 0; - } - - beforeAll(async function () { - // Create the test window - testWindow = await SpecRunnerUtils.createTestWindowAndRun(); - // Get reference to all the required modules - $ = testWindow.$; - PreferencesManager = testWindow.brackets.test.PreferencesManager; - FileSystem = testWindow.brackets.test.FileSystem; - MainViewManager = testWindow.brackets.test.MainViewManager; - CommandManager = testWindow.brackets.test.CommandManager; - Commands = testWindow.brackets.test.Commands; - DocumentManager = testWindow.brackets.test.DocumentManager; - Strings = testWindow.Strings; - - // Create test files - testFilePath = SpecRunnerUtils.getTempDirectory() + "/tabbar-test.js"; - testFilePath2 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test2.js"; - testFilePath3 = SpecRunnerUtils.getTempDirectory() + "/tabbar-test3.js"; - - // Create files with the same name in different directories for testing duplicate name handling - testDuplicateDir1 = SpecRunnerUtils.getTempDirectory() + "/dir1"; - testDuplicateDir2 = SpecRunnerUtils.getTempDirectory() + "/dir2"; - testDuplicateName = "duplicate.js"; - - await SpecRunnerUtils.createTempDirectory(); - await SpecRunnerUtils.ensureExistsDirAsync(testDuplicateDir1); - await SpecRunnerUtils.ensureExistsDirAsync(testDuplicateDir2); - - await jsPromise(SpecRunnerUtils.createTextFile(testFilePath, "// Test file 1 for TabBar", FileSystem)); - await jsPromise(SpecRunnerUtils.createTextFile(testFilePath2, "// Test file 2 for TabBar", FileSystem)); - await jsPromise(SpecRunnerUtils.createTextFile(testFilePath3, "// Test file 3 for TabBar", FileSystem)); - await jsPromise( - SpecRunnerUtils.createTextFile( - testDuplicateDir1 + "/" + testDuplicateName, - "// Duplicate file 1", - FileSystem - ) - ); - await jsPromise( - SpecRunnerUtils.createTextFile( - testDuplicateDir2 + "/" + testDuplicateName, - "// Duplicate file 2", - FileSystem - ) - ); - - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - }, 30000); - - afterAll(async function () { - // Close all files without prompting to save - await testWindow.closeAllFiles(); - - testWindow = null; - await SpecRunnerUtils.closeTestWindow(); - await SpecRunnerUtils.removeTempDirectory(); - }, 30000); - - /** - * Helper function to check if a tab for a specific file exists in the tab bar - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab exists, false otherwise - */ - function tabExists(filePath) { - return $(`.tab[data-path="${filePath}"]`).length > 0; - } - - /** - * Helper function to count the number of tabs in the tab bar - * @returns {number} - The number of tabs - */ - function getTabCount() { - return $(".tab").length; - } - - /** - * Helper function to check if a tab for a specific file is active - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab is active, false otherwise - */ - function isTabActive(filePath) { - return $(`.tab[data-path="${filePath}"].active`).length > 0; - } - - /** - * Helper function to get the tab element for a specific file - * @param {string} filePath - The path of the file - * @returns {jQuery} - The tab element - */ - function getTab(filePath) { - return $(`.tab[data-path="${filePath}"]`); - } - - /** - * Helper function to check if a tab has a dirty indicator - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab has a dirty indicator, false otherwise - */ - function isTabDirty(filePath) { - return getTab(filePath).hasClass("dirty"); - } - - /** - * Helper function to get the tab name element for a specific file - * @param {string} filePath - The path of the file - * @returns {jQuery} - The tab name element - */ - function getTabName(filePath) { - return getTab(filePath).find(".tab-name"); - } - - /** - * Helper function to check if a tab has a directory name displayed - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab has a directory name, false otherwise - */ - function hasDirectoryName(filePath) { - return getTab(filePath).find(".tab-dirname").length > 0; - } - - /** - * Helper function to get the directory name displayed in a tab - * @param {string} filePath - The path of the file - * @returns {string} - The directory name or empty string if not found - */ - function getDirectoryName(filePath) { - const $dirName = getTab(filePath).find(".tab-dirname"); - return $dirName.length ? $dirName.text() : ""; - } - - /** - * Helper function to check if a tab has a file icon - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab has a file icon, false otherwise - */ - function hasFileIcon(filePath) { - return getTab(filePath).find(".tab-icon i").length > 0; - } - - /** - * Helper function to check if a tab has a git status indicator - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab has a git status indicator, false otherwise - */ - function hasGitStatus(filePath) { - return getTab(filePath).hasClass("git-new") || getTab(filePath).hasClass("git-modified"); - } - - /** - * Helper function to get the tooltip (title attribute) of a tab - * @param {string} filePath - The path of the file - * @returns {string} - The tooltip text - */ - function getTabTooltip(filePath) { - return getTab(filePath).attr("title") || ""; - } - - /** - * Helper function to check if a tab has a close button - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab has a close button, false otherwise - */ - function hasCloseButton(filePath) { - return getTab(filePath).find(".tab-close").length > 0; - } - - /** - * Helper function to check if the overflow button is visible - * @returns {boolean} - True if the overflow button is visible, false otherwise - */ - function isOverflowButtonVisible() { - return $("#overflow-button").is(":visible"); - } - - /** - * Helper function to get the overflow button element - * @returns {jQuery} - The overflow button element - */ - function getOverflowButton() { - return $("#overflow-button"); - } - - /** - * Helper function to check if a tab is visible in the tab bar (not hidden by overflow) - * @param {string} filePath - The path of the file to check - * @returns {boolean} - True if the tab is visible, false otherwise - */ - function isTabVisible(filePath) { - const $tab = getTab(filePath); - if (!$tab.length) { - return false; - } - - const $tabBar = $("#phoenix-tab-bar"); - const tabBarRect = $tabBar[0].getBoundingClientRect(); - const tabRect = $tab[0].getBoundingClientRect(); - - // A tab is considered visible if it is completely within the tab bar's visible area - // with a small margin of error (2px) - return tabRect.left >= tabBarRect.left && tabRect.right <= tabBarRect.right + 2; - } - - /** - * Helper function to get the overflow dropdown menu - * @returns {jQuery} - The overflow dropdown menu element - */ - function getOverflowDropdown() { - return $(".dropdown-overflow-menu"); - } - - /** - * Helper function to get the items in the overflow dropdown - * @returns {jQuery} - The overflow dropdown items - */ - function getOverflowDropdownItems() { - return $(".dropdown-overflow-menu .dropdown-tab-item"); - } - - /** - * Helper function to get a specific item in the overflow dropdown by file path - * @param {string} filePath - The path of the file to find - * @returns {jQuery} - The dropdown item element - */ - function getOverflowDropdownItem(filePath) { - return $(`.dropdown-overflow-menu .dropdown-tab-item[data-tab-path="${filePath}"]`); - } - - describe("Visibility", function () { - it("should show tab bar when the feature is enabled", async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Wait for the tab bar to become visible - await awaitsFor( - function () { - return $("#phoenix-tab-bar").is(":visible"); - }, - "Tab bar to become visible", - 1000 - ); - - // Verify the tab bar is visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(true); - }); - - it("should hide tab bar when the feature is disabled", async function () { - // Disable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: false, numberOfTabs: -1 }); - - // Wait for the tab bar to become hidden - await awaitsFor( - function () { - return !$("#phoenix-tab-bar").is(":visible"); - }, - "Tab bar to become hidden", - 1000 - ); - - // Verify the tab bar is not visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); - }); - - it("should show working set when the option is enabled", async function () { - // Enable the working set feature - PreferencesManager.set("showWorkingSet", true); - - // Wait for the working set to become visible - await awaitsFor( - function () { - return !$("#working-set-list-container").hasClass("working-set-hidden"); - }, - "Working set to become visible", - 1000 - ); - - // Verify the working set is visible - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); - }); - - it("should hide working set when the option is disabled", async function () { - // Disable the working set feature - PreferencesManager.set("showWorkingSet", false); - - // Wait for the working set to become hidden - await awaitsFor( - function () { - return $("#working-set-list-container").hasClass("working-set-hidden"); - }, - "Working set to become hidden", - 1000 - ); - - // Verify the working set is not visible - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); - }); - }); - - describe("Configure Working Set Button", function () { - it("should have a working set configuration button in the sidebar", function () { - // Verify the button exists - const $configButton = $(".working-set-splitview-btn"); - expect($configButton.length).toBe(1); - }); - - it("should open a menu with 'Show working set' and 'Show file tab bar' options when clicked", async function () { - // Click the configure working set button - const $configButton = $(".working-set-splitview-btn"); - $configButton.click(); - - // Wait for the menu to appear - await awaitsFor( - function () { - return $(".dropdown-menu:visible").length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Verify the menu contains the expected options - const $menu = $(".dropdown-menu:visible"); - const showWorkingSetItem = $menu.find("li a[id$='cmd.toggleShowWorkingSet']"); - const showFileTabsItem = $menu.find("li a[id$='cmd.toggleShowFileTabs']"); - - expect(showWorkingSetItem.length).toBe(1); - expect(showFileTabsItem.length).toBe(1); - - // Clean up - close the menu - $("body").click(); - }); - - it("should toggle working set visibility when 'Show working set' option is clicked", async function () { - // First, ensure working set is visible - PreferencesManager.set("showWorkingSet", true); - await awaitsFor( - function () { - return !$("#working-set-list-container").hasClass("working-set-hidden"); - }, - "Working set to become visible", - 1000 - ); - - // Click the configure working set button - const $configButton = $(".working-set-splitview-btn"); - $configButton.click(); - - // Wait for the menu to appear - await awaitsFor( - function () { - return $(".dropdown-menu:visible").length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Click the "Show working set" option - const $menu = $(".dropdown-menu:visible"); - const showWorkingSetItem = $menu.find("li a[id$='cmd.toggleShowWorkingSet']"); - showWorkingSetItem.click(); - - // Wait for the working set to become hidden - await awaitsFor( - function () { - return $("#working-set-list-container").hasClass("working-set-hidden"); - }, - "Working set to become hidden", - 1000 - ); - - // Verify the working set is hidden - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); - - // Click the configure working set button again - $configButton.click(); - - // Wait for the menu to appear - await awaitsFor( - function () { - return $(".dropdown-menu:visible").length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Click the "Show working set" option again - const $menu2 = $(".dropdown-menu:visible"); - const showWorkingSetItem2 = $menu2.find("li a[id$='cmd.toggleShowWorkingSet']"); - showWorkingSetItem2.click(); - - // Wait for the working set to become visible - await awaitsFor( - function () { - return !$("#working-set-list-container").hasClass("working-set-hidden"); - }, - "Working set to become visible", - 1000 - ); - - // Verify the working set is visible - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); - }); - - it("should toggle tab bar visibility when 'Show file tab bar' option is clicked", async function () { - // First, ensure tab bar is visible - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - await awaitsFor( - function () { - return $("#phoenix-tab-bar").is(":visible"); - }, - "Tab bar to become visible", - 1000 - ); - - // Click the configure working set button - const $configButton = $(".working-set-splitview-btn"); - $configButton.click(); - - // Wait for the menu to appear - await awaitsFor( - function () { - return $(".dropdown-menu:visible").length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Click the "Show file tab bar" option - const $menu = $(".dropdown-menu:visible"); - const showFileTabsItem = $menu.find("li a[id$='cmd.toggleShowFileTabs']"); - showFileTabsItem.click(); - - // Wait for the tab bar to become hidden - await awaitsFor( - function () { - return !$("#phoenix-tab-bar").is(":visible"); - }, - "Tab bar to become hidden", - 1000 - ); - - // Verify the tab bar is hidden - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); - - // Click the configure working set button again - $configButton.click(); - - // Wait for the menu to appear - await awaitsFor( - function () { - return $(".dropdown-menu:visible").length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Click the "Show file tab bar" option again - const $menu2 = $(".dropdown-menu:visible"); - const showFileTabsItem2 = $menu2.find("li a[id$='cmd.toggleShowFileTabs']"); - showFileTabsItem2.click(); - - // Wait for the tab bar to become visible - await awaitsFor( - function () { - return $("#phoenix-tab-bar").is(":visible"); - }, - "Tab bar to become visible", - 1000 - ); - - // Verify the tab bar is visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(true); - }); - }); - - describe("Drag and Drop", function () { - beforeEach(async function () { - // Close all files and reset to single pane - await testWindow.closeAllFiles(); - MainViewManager.setLayoutScheme(1, 1); - - // Wait for cleanup to complete - await awaitsFor( - function () { - return MainViewManager.getPaneCount() === 1 && getTabCount() === 0; - }, - "Cleanup to complete with single pane and no tabs", - 1000 - ); - }); - - it("should allow dragging and dropping a tab to the beginning of the tab bar", async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Create and open multiple test files to work with - const testFiles = await createTestFiles(3, "drag-drop-test", "// Drag drop test file"); - await openTestFiles(testFiles); - await waitForTabs(testFiles); - - // Verify initial tab order - const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); - expect(initialWorkingSet.length).toBe(testFiles.length); - expect(initialWorkingSet[0].fullPath).toBe(testFiles[0]); - expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); - expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); - - // Simulate drag and drop from first tab to last tab - await simulateTabDragAndDrop(testFiles[0], testFiles[2], true); - - // Wait for the working set to update - await awaitsFor( - function () { - const currentWorkingSet = MainViewManager.getWorkingSet("first-pane"); - // Check if the first file has moved to before the last file - return ( - currentWorkingSet.length === testFiles.length && - currentWorkingSet[1].fullPath === testFiles[0] - ); - }, - "Working set to update after drag and drop", - 1000 - ); - - // Verify the new tab order - const finalWorkingSet = MainViewManager.getWorkingSet("first-pane"); - expect(finalWorkingSet.length).toBe(testFiles.length); - expect(finalWorkingSet[0].fullPath).toBe(testFiles[1]); - expect(finalWorkingSet[1].fullPath).toBe(testFiles[0]); - expect(finalWorkingSet[2].fullPath).toBe(testFiles[2]); - - // Clean up - close all the test files - await closeTestFiles(testFiles); - }); - - it("should allow dragging and dropping a tab in between other tabs", async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Create and open multiple test files to work with - const testFiles = await createTestFiles(3, "drag-between-test", "// Drag between test file"); - await openTestFiles(testFiles); - await waitForTabs(testFiles); - - // Verify initial tab order - const initialWorkingSet = MainViewManager.getWorkingSet("first-pane"); - expect(initialWorkingSet.length).toBe(testFiles.length); - expect(initialWorkingSet[0].fullPath).toBe(testFiles[0]); - expect(initialWorkingSet[1].fullPath).toBe(testFiles[1]); - expect(initialWorkingSet[2].fullPath).toBe(testFiles[2]); - - // Simulate drag and drop from last tab to before the middle tab - await simulateTabDragAndDrop(testFiles[2], testFiles[1], true); - - // Wait for the working set to update - await awaitsFor( - function () { - const currentWorkingSet = MainViewManager.getWorkingSet("first-pane"); - // Check if the last file has moved to between the first and second files - return ( - currentWorkingSet.length === testFiles.length && - currentWorkingSet[1].fullPath === testFiles[2] - ); - }, - "Working set to update after drag and drop", - 1000 - ); - - // Verify the new tab order - const finalWorkingSet = MainViewManager.getWorkingSet("first-pane"); - expect(finalWorkingSet.length).toBe(testFiles.length); - expect(finalWorkingSet[0].fullPath).toBe(testFiles[0]); - expect(finalWorkingSet[1].fullPath).toBe(testFiles[2]); // Last tab should now be in the middle - expect(finalWorkingSet[2].fullPath).toBe(testFiles[1]); // Middle tab should now be last - - // Clean up - close all the test files - await closeTestFiles(testFiles); - }); - - it("should allow dragging a tab from one pane to another non-empty pane", async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Set up a horizontal split view (two columns) - MainViewManager.setLayoutScheme(1, 2); - - // Create test files for both panes - const firstPaneFiles = await createTestFiles(2, "first-pane-test", "// First pane test file"); - const secondPaneFiles = await createTestFiles(2, "second-pane-test", "// Second pane test file"); - - // Open files in both panes - await openTestFiles(firstPaneFiles, "first-pane"); - await openTestFiles(secondPaneFiles, "second-pane"); - - // Wait for all tabs to appear in both panes - await waitForTabs(firstPaneFiles, "first-pane"); - await waitForTabs(secondPaneFiles, "second-pane"); - - // Verify initial tab counts - expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); - expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length); - - // Get the source tab from the first pane and target tab from the second pane - const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); - const targetTab = $(`.tab[data-path="${secondPaneFiles[0]}"]`); - - // Simulate drag start on the source tab - const dragStartEvent = $.Event("dragstart", { - originalEvent: { - dataTransfer: { - setData: function () {}, - effectAllowed: "move" - } - } - }); - sourceTab.trigger(dragStartEvent); - - // Simulate dragenter on the target tab - const dragEnterEvent = $.Event("dragenter"); - targetTab.trigger(dragEnterEvent); - - // Simulate drag over on the target tab - const dragOverEvent = $.Event("dragover", { - originalEvent: { - dataTransfer: { - dropEffect: "move" - }, - clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge - }, - preventDefault: function () {} - }); - targetTab.trigger(dragOverEvent); - - // Simulate drop on the target tab - const dropEvent = $.Event("drop", { - originalEvent: { - dataTransfer: {}, - clientX: targetTab[0].getBoundingClientRect().left + 5 // Position near the left edge - }, - preventDefault: function () {}, - stopPropagation: function () {} - }); - targetTab.trigger(dropEvent); - - // Simulate dragend to complete the operation - const dragEndEvent = $.Event("dragend"); - sourceTab.trigger(dragEndEvent); - - // Wait for the tab to move to the second pane - await awaitsFor( - function () { - return ( - !tabExistsInPane(firstPaneFiles[0], "first-pane") && - tabExistsInPane(firstPaneFiles[0], "second-pane") - ); - }, - "Tab to move from first pane to second pane", - 1000 - ); - - // Verify the tab counts after the drag and drop - expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); - expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length + 1); - - // Verify the tab is now in the second pane - expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); - expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); - - // Clean up - close all files and reset to single pane - await testWindow.closeAllFiles(); - MainViewManager.setLayoutScheme(1, 1); - }); - - it("should allow dragging a tab to an empty pane", async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Set up a horizontal split view (two columns) - MainViewManager.setLayoutScheme(1, 2); - - // Wait for layout to settle and ensure second pane is empty - await awaitsFor( - function () { - return MainViewManager.getPaneCount() === 2; - }, - "Layout to settle with two panes", - 1000 - ); - - // Force close any files that might be open in the second pane - const secondPaneWorkingSet = MainViewManager.getWorkingSet("second-pane"); - for (const file of secondPaneWorkingSet) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: file, paneId: "second-pane" }), - `Force close file ${file.fullPath} in second pane` - ); - } - - // Wait for the second pane to actually be empty after cleanup - await awaitsFor( - function () { - return getPaneTabCount("second-pane") === 0; - }, - "Second pane to be cleaned up", - 2000 - ); - - expect(getPaneTabCount("second-pane")).toBe(0); - - // Create test files for the first pane - const firstPaneFiles = await createTestFiles( - 2, - "first-pane-empty-test", - "// First pane empty test file" - ); - - // Open files in the first pane only - await openTestFiles(firstPaneFiles, "first-pane"); - - // Wait for all tabs to appear in the first pane - await waitForTabs(firstPaneFiles, "first-pane"); - - expect(getPaneTabCount("second-pane")).toBe(0); - - // Ensure second pane is empty before proceeding - await awaitsFor( - function () { - return getPaneTabCount("second-pane") === 0 && !isTabBarVisible("second-pane"); - }, - "Second pane to be empty", - 1000 - ); - - // Verify initial tab counts - expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length); - expect(getPaneTabCount("second-pane")).toBe(0); - expect(isTabBarVisible("second-pane")).toBe(false); - - // Get the source tab from the first pane - const sourceTab = $(`.tab[data-path="${firstPaneFiles[0]}"]`); - - // Get the empty pane content area as the drop target - const emptyPaneTarget = $("#second-pane .pane-content"); - - // Simulate drag start on the source tab - const dragStartEvent = $.Event("dragstart", { - originalEvent: { - dataTransfer: { - setData: function () {}, - effectAllowed: "move" - } - } - }); - sourceTab.trigger(dragStartEvent); - - // Simulate dragenter on the empty pane - const dragEnterEvent = $.Event("dragenter"); - emptyPaneTarget.trigger(dragEnterEvent); - - // Simulate drag over on the empty pane - const dragOverEvent = $.Event("dragover", { - originalEvent: { - dataTransfer: { - dropEffect: "move" - } - }, - preventDefault: function () {} - }); - emptyPaneTarget.trigger(dragOverEvent); - - // Simulate drop on the empty pane - const dropEvent = $.Event("drop", { - originalEvent: { - dataTransfer: {} - }, - preventDefault: function () {}, - stopPropagation: function () {} - }); - emptyPaneTarget.trigger(dropEvent); - - // Simulate dragend to complete the operation - const dragEndEvent = $.Event("dragend"); - sourceTab.trigger(dragEndEvent); - - // Wait for the tab to move to the second pane - await awaitsFor( - function () { - return ( - !tabExistsInPane(firstPaneFiles[0], "first-pane") && - tabExistsInPane(firstPaneFiles[0], "second-pane") && - isTabBarVisible("second-pane") - ); - }, - "Tab to move from first pane to second pane and tab bar to appear", - 1000 - ); - - // Verify the tab counts after the drag and drop - expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); - expect(getPaneTabCount("second-pane")).toBe(1); - - // Verify the tab is now in the second pane - expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); - expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); - - // Clean up - close all files and reset to single pane - await testWindow.closeAllFiles(); - MainViewManager.setLayoutScheme(1, 1); - }); - }); - - describe("Working Set", function () { - beforeEach(async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Wait for the tab bar to update - await awaitsFor( - function () { - return $("#phoenix-tab-bar").length > 0 && getTabCount() === 0; - }, - "Tab bar to update with no tabs", - 1000 - ); - }); - - it("should add tabs when files are added to the working set", async function () { - // Open the first test file and wait for its tab to appear - await openTestFiles([testFilePath]); - await waitForTabs([testFilePath]); - - // Verify the tab exists - expect(tabExists(testFilePath)).toBe(true); - expect(getTabCount()).toBe(1); - - // Open the second test file and wait for its tab to appear - await openTestFiles([testFilePath2]); - await waitForTabs([testFilePath2]); - - // Verify both tabs exist - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(true); - expect(getTabCount()).toBe(2); - - // Open the third test file and wait for its tab to appear - await openTestFiles([testFilePath3]); - await waitForTabs([testFilePath3]); - - // Verify all three tabs exist - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(true); - expect(tabExists(testFilePath3)).toBe(true); - expect(getTabCount()).toBe(3); - }); - - it("should remove tabs when files are removed from the working set", async function () { - // Open all three test files - const testFiles = [testFilePath, testFilePath2, testFilePath3]; - await openTestFiles(testFiles); - await waitForTabs(testFiles); - - // Verify all three tabs exist - expect(getTabCount()).toBe(3); - - // Close the second test file - await closeTestFiles([testFilePath2]); - - // Wait for the tab to disappear - await awaitsFor( - function () { - return !tabExists(testFilePath2); - }, - "Tab for second file to disappear", - 1000 - ); - - // Verify the second tab is removed - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(false); - expect(tabExists(testFilePath3)).toBe(true); - expect(getTabCount()).toBe(2); - - // Close the first test file - await closeTestFiles([testFilePath]); - - // Wait for the tab to disappear - await awaitsFor( - function () { - return !tabExists(testFilePath); - }, - "Tab for first file to disappear", - 1000 - ); - - // Verify the first tab is removed - expect(tabExists(testFilePath)).toBe(false); - expect(tabExists(testFilePath2)).toBe(false); - expect(tabExists(testFilePath3)).toBe(true); - expect(getTabCount()).toBe(1); - - // Close the third test file - await closeTestFiles([testFilePath3]); - - // Wait for the tab to disappear - await awaitsFor( - function () { - return !tabExists(testFilePath3); - }, - "Tab for third file to disappear", - 1000 - ); - - // Verify all tabs are removed - expect(tabExists(testFilePath)).toBe(false); - expect(tabExists(testFilePath2)).toBe(false); - expect(tabExists(testFilePath3)).toBe(false); - expect(getTabCount()).toBe(0); - }); - }); - - describe("Active Tab", function () { - beforeEach(async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Open all three test files - const testFiles = [testFilePath, testFilePath2, testFilePath3]; - await openTestFiles(testFiles); - await waitForTabs(testFiles); - }); - - it("should change active tab when switching files in the working set", async function () { - // Helper function to switch to a file and verify it's active - async function switchToFileAndVerify(filePath, description) { - // Switch to the file - await openTestFiles([filePath]); - - // Wait for the tab to become active - await awaitsFor( - function () { - return isTabActive(filePath); - }, - `${description} to become active`, - 1000 - ); - - // Verify this tab is active and others are not - expect(isTabActive(testFilePath)).toBe(filePath === testFilePath); - expect(isTabActive(testFilePath2)).toBe(filePath === testFilePath2); - expect(isTabActive(testFilePath3)).toBe(filePath === testFilePath3); - } - - // Test switching to each file - await switchToFileAndVerify(testFilePath, "First tab"); - await switchToFileAndVerify(testFilePath2, "Second tab"); - await switchToFileAndVerify(testFilePath3, "Third tab"); - }); - - it("should display active tab correctly based on the active file in the working set", async function () { - // Get the currently active file - const activeFile = MainViewManager.getCurrentlyViewedFile(); - - // Wait for tab bar to be recreated and reflect the active file - await awaitsFor( - function () { - return $("#phoenix-tab-bar").length > 0 && activeFile && isTabActive(activeFile.fullPath); - }, - "Tab bar to be recreated and show active file", - 1000 - ); - - // Verify the tab for the active file is active - expect(isTabActive(activeFile.fullPath)).toBe(true); - - // Switch to a different file - await openTestFiles([testFilePath2]); - - // Get the new active file - const newActiveFile = MainViewManager.getCurrentlyViewedFile(); - - // Wait for tab bar to update with the new active file - await awaitsFor( - function () { - return newActiveFile && isTabActive(newActiveFile.fullPath); - }, - "Tab bar to update with new active file", - 1000 - ); - - // Verify the tab for the new active file is active - expect(isTabActive(newActiveFile.fullPath)).toBe(true); - - // Verify the tab for the previous active file is no longer active - expect(isTabActive(activeFile.fullPath)).toBe(false); - }); - - it("should switch files properly when different tabs are clicked", async function () { - // Helper function to click a tab and verify it becomes active - async function clickTabAndVerify(filePath, description) { - const $tab = getTab(filePath); - expect($tab.length).toBe(1); - $tab.trigger("mousedown"); - - // Wait for the file to become active - await awaitsFor( - function () { - return ( - isTabActive(filePath) && MainViewManager.getCurrentlyViewedFile().fullPath === filePath - ); - }, - `${description} to become active after tab click`, - 1000 - ); - - // Verify this tab is active and others are not - const allPaths = [testFilePath, testFilePath2, testFilePath3]; - allPaths.forEach((path) => { - expect(isTabActive(path)).toBe(path === filePath); - }); - - // Verify the correct file is loaded in the editor - expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(filePath); - } - // Wait for the tab bar to be properly loaded - await awaitsFor( - function () { - return $("#phoenix-tab-bar").length > 0 && getTabCount() === 3; - }, - "Tab bar to be properly loaded with all tabs", - 1000 - ); - - // Wait for the third file to become active - await awaitsFor( - function () { - return isTabActive(testFilePath3) && - MainViewManager.getCurrentlyViewedFile() && - MainViewManager.getCurrentlyViewedFile().fullPath === testFilePath3; - }, - "Third file to become active", - 2000 - ); - - // Initially, verify the third file is active (last opened) - expect(isTabActive(testFilePath3)).toBe(true); - expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testFilePath3); - - // Test clicking on each tab - await clickTabAndVerify(testFilePath, "First file"); - await clickTabAndVerify(testFilePath2, "Second file"); - await clickTabAndVerify(testFilePath3, "Third file"); - - // Click back on the first tab to ensure it still works - await clickTabAndVerify(testFilePath, "First file"); - }); - }); - - describe("Overflow", function () { - beforeEach(async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - }); - - it("should show overflow button when there are too many tabs to fit", async function () { - // Create several test files to ensure overflow - const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); - - // Open all the test files - await openTestFiles(testFiles); - - // Wait for all tabs to appear - await waitForTabs(testFiles); - - // Wait for the overflow button to appear - await awaitsFor( - function () { - return isOverflowButtonVisible(); - }, - "Overflow button to appear", - 1000 - ); - - // Verify the overflow button is visible - expect(isOverflowButtonVisible()).toBe(true); - - // Verify that some tabs are not visible - let visibleTabs = 0; - let hiddenTabs = 0; - for (const filePath of testFiles) { - if (isTabVisible(filePath)) { - visibleTabs++; - } else { - hiddenTabs++; - } - } - - // There should be at least one hidden tab - expect(hiddenTabs).toBeGreaterThan(0); - expect(visibleTabs + hiddenTabs).toBe(testFiles.length); - - // Clean up - close all the test files - await closeTestFiles(testFiles); - }); - - it("should display dropdown with hidden tabs when overflow button is clicked", async function () { - // Create several test files to ensure overflow - const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); - - // Open all the test files - await openTestFiles(testFiles); - - // Wait for all tabs to appear - await waitForTabs(testFiles); - - // Wait for the overflow button to appear - await awaitsFor( - function () { - return isOverflowButtonVisible(); - }, - "Overflow button to appear", - 1000 - ); - - // Get the list of hidden tabs - const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); - expect(hiddenFiles.length).toBeGreaterThan(0); - - // Click the overflow button - getOverflowButton().click(); - - // Wait for the dropdown to appear - await awaitsFor( - function () { - return getOverflowDropdown().length > 0; - }, - "Overflow dropdown to appear", - 1000 - ); - - // Verify the dropdown is visible - expect(getOverflowDropdown().length).toBeGreaterThan(0); - - // Verify the dropdown contains items for all hidden tabs - const dropdownItems = getOverflowDropdownItems(); - expect(dropdownItems.length).toBe(hiddenFiles.length); - - // Verify each hidden file has an item in the dropdown - for (const filePath of hiddenFiles) { - const item = getOverflowDropdownItem(filePath); - expect(item.length).toBe(1); - } - - // Clean up - close the dropdown by clicking elsewhere - $("body").click(); - - // Wait for the dropdown to disappear - await awaitsFor( - function () { - return getOverflowDropdown().length === 0; - }, - "Overflow dropdown to disappear", - 1000 - ); - - // Clean up - close all the test files - await closeTestFiles(testFiles); - }); - - it("should make tab visible and file active when clicking on item in overflow dropdown", async function () { - // Create several test files to ensure overflow - const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); - - // Open all the test files - await openTestFiles(testFiles); - - // Wait for all tabs to appear - await waitForTabs(testFiles); - - // Wait for the overflow button to appear - await awaitsFor( - function () { - return isOverflowButtonVisible(); - }, - "Overflow button to appear", - 1000 - ); - - // Get the list of hidden tabs - const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); - expect(hiddenFiles.length).toBeGreaterThan(0); - - // Select a hidden file to test - const testHiddenFile = hiddenFiles[0]; - - // Click the overflow button - getOverflowButton().click(); - - // Wait for the dropdown to appear - await awaitsFor( - function () { - return getOverflowDropdown().length > 0; - }, - "Overflow dropdown to appear", - 1000 - ); - - // Get the dropdown item for the test file - const dropdownItem = getOverflowDropdownItem(testHiddenFile); - expect(dropdownItem.length).toBe(1); - - // Click the dropdown item - dropdownItem.click(); - - // Wait for the file to become active - await awaitsFor( - function () { - return ( - isTabActive(testHiddenFile) && - MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile - ); - }, - "Hidden file to become active after dropdown item click", - 1000 - ); - - // Verify the file is active - expect(isTabActive(testHiddenFile)).toBe(true); - expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testHiddenFile); - - // Verify the tab is now visible (scrolled into view) - await awaitsFor( - function () { - return isTabVisible(testHiddenFile); - }, - "Tab to become visible after dropdown item click", - 1000 - ); - - expect(isTabVisible(testHiddenFile)).toBe(true); - - // Clean up - close all the test files - await closeTestFiles(testFiles); - }); - - it("should scroll tab bar to make selected file visible when selecting from working set", async function () { - // Create several test files to ensure overflow - const testFiles = await createTestFiles(15, "overflow-test", "// Overflow test file"); - - // Open all the test files - await openTestFiles(testFiles); - - // Wait for all tabs to appear - await waitForTabs(testFiles); - - // Wait for the overflow button to appear - await awaitsFor( - function () { - return isOverflowButtonVisible(); - }, - "Overflow button to appear", - 1000 - ); - - // Get the list of hidden tabs - const hiddenFiles = testFiles.filter((filePath) => !isTabVisible(filePath)); - expect(hiddenFiles.length).toBeGreaterThan(0); - - // Select a hidden file to test - const testHiddenFile = hiddenFiles[0]; - - // Verify the tab is not visible initially - expect(isTabVisible(testHiddenFile)).toBe(false); - - // Select the file directly from the working set (not using the overflow dropdown) - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testHiddenFile }), - "Open hidden file from working set" - ); - - // Wait for the file to become active - await awaitsFor( - function () { - return ( - isTabActive(testHiddenFile) && - MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile - ); - }, - "Hidden file to become active after selection from working set", - 1000 - ); - - // Verify the file is active - expect(isTabActive(testHiddenFile)).toBe(true); - expect(MainViewManager.getCurrentlyViewedFile().fullPath).toBe(testHiddenFile); - - // Verify the tab is now visible (scrolled into view) - await awaitsFor( - function () { - return isTabVisible(testHiddenFile); - }, - "Tab to become visible after selection from working set", - 1000 - ); - - expect(isTabVisible(testHiddenFile)).toBe(true); - - // Clean up - close all the test files - await closeTestFiles(testFiles); - }); - }); - - describe("Tab Items", function () { - beforeEach(async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - }); - - it("should display the correct tab name", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - - // Wait for the tab to appear - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab to appear", - 1000 - ); - - // Get the filename from the path - const fileName = testFilePath.split("/").pop(); - - // Verify the tab name is correct - expect(getTabName(testFilePath).text()).toBe(fileName); - }); - - it("should display a file icon", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - - // Wait for the tab to appear - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab to appear", - 1000 - ); - - // Verify the tab has a file icon - expect(hasFileIcon(testFilePath)).toBe(true); - }); - - it("should display a dirty indicator when the file is modified and remove it when saved", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - - // Wait for the tab to appear - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab to appear", - 1000 - ); - - // Initially, the file should not be dirty - expect(isTabDirty(testFilePath)).toBe(false); - - // Get the document and modify it - const doc = DocumentManager.getOpenDocumentForPath(testFilePath); - doc.setText("// Modified content"); - - // Wait for the dirty indicator to appear - await awaitsFor( - function () { - return isTabDirty(testFilePath); - }, - "Dirty indicator to appear", - 1000 - ); - - // Verify the tab has a dirty indicator - expect(isTabDirty(testFilePath)).toBe(true); - - // Save the file - await awaitsForDone(CommandManager.execute(Commands.FILE_SAVE, { doc: doc }), "Save file"); - - // Wait for the dirty indicator to disappear - await awaitsFor( - function () { - return !isTabDirty(testFilePath); - }, - "Dirty indicator to disappear", - 1000 - ); - - // Verify the tab no longer has a dirty indicator - expect(isTabDirty(testFilePath)).toBe(false); - - // Revert the changes for cleanup - doc.setText("// Test file 1 for TabBar"); - await awaitsForDone( - CommandManager.execute(Commands.FILE_SAVE, { doc: doc }), - "Save file with original content" - ); - }); - - it("should display directory name for files with the same name", async function () { - // Open both duplicate files - const duplicateFile1 = testDuplicateDir1 + "/" + testDuplicateName; - const duplicateFile2 = testDuplicateDir2 + "/" + testDuplicateName; - - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: duplicateFile1 }), - "Open first duplicate file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: duplicateFile2 }), - "Open second duplicate file" - ); - - // Wait for both tabs to appear - await awaitsFor( - function () { - return tabExists(duplicateFile1) && tabExists(duplicateFile2); - }, - "Both duplicate tabs to appear", - 1000 - ); - - // Verify both tabs have directory names - expect(hasDirectoryName(duplicateFile1)).toBe(true); - expect(hasDirectoryName(duplicateFile2)).toBe(true); - - // Verify the directory names are correct - expect(getDirectoryName(duplicateFile1)).toContain("dir1"); - expect(getDirectoryName(duplicateFile2)).toContain("dir2"); - }); - - it("should display the full file path in the tooltip", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - - // Wait for the tab to appear - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab to appear", - 1000 - ); - - // Verify the tooltip contains the full path - const tooltip = getTabTooltip(testFilePath); - expect(tooltip).toContain(Phoenix.app.getDisplayPath(testFilePath)); - }); - - it("should display git change markers when git is enabled", async function () { - // Skip this test if Git integration is not available - if (!testWindow.brackets.test.Phoenix || !testWindow.brackets.test.Phoenix.app) { - expect("Test skipped - Phoenix.app not available").toBe("Test skipped - Phoenix.app not available"); - return; - } - - // Create a mock for the Git integration - if (!testWindow.phoenixGitEvents) { - testWindow.phoenixGitEvents = {}; - } - - // Save the original Git integration if it exists - const originalGitEvents = testWindow.phoenixGitEvents.TabBarIntegration; - - // Create a mock TabBarIntegration - testWindow.phoenixGitEvents.TabBarIntegration = { - isUntracked: function (path) { - return path === testFilePath; // Mark the first file as untracked - }, - isModified: function (path) { - return path === testFilePath2; // Mark the second file as modified - } - }; - - // Make sure the EventEmitter exists - if (!testWindow.phoenixGitEvents.EventEmitter) { - testWindow.phoenixGitEvents.EventEmitter = { - on: function () {}, - emit: function () {} - }; - } - - // Open the test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - - // Trigger a Git status update - testWindow.phoenixGitEvents.EventEmitter.emit("GIT_FILE_STATUS_CHANGED"); - - // Wait for the tabs to update with Git status - await awaitsFor( - function () { - return hasGitStatus(testFilePath) && hasGitStatus(testFilePath2); - }, - "Tabs to update with Git status", - 1000 - ); - - // Verify the first file has the git-new class - const $tab1 = getTab(testFilePath); - expect($tab1.hasClass("git-new")).toBe(true); - expect(hasGitStatus(testFilePath)).toBe(true); - - // Verify the second file has the git-modified class - const $tab2 = getTab(testFilePath2); - expect($tab2.hasClass("git-modified")).toBe(true); - expect(hasGitStatus(testFilePath2)).toBe(true); - - // Verify the tooltips contain the Git status - expect(getTabTooltip(testFilePath)).toContain("Untracked"); - expect(getTabTooltip(testFilePath2)).toContain("Modified"); - - // Restore the original Git integration - testWindow.phoenixGitEvents.TabBarIntegration = originalGitEvents; - }); - - it("should display a close button", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - - // Wait for the tab to appear - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab to appear", - 1000 - ); - - // Verify the tab has a close button - expect(hasCloseButton(testFilePath)).toBe(true); - - // Verify the close button has the correct icon - const $closeButton = getTab(testFilePath).find(".tab-close"); - expect($closeButton.find("i.fa-times").length).toBe(1); - }); - - it("should close the file when the close button is clicked", async function () { - // Open the first test file - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open test file" - ); - - // Wait for the tab to appear - await awaitsFor( - function () { - return tabExists(testFilePath); - }, - "Tab to appear", - 1000 - ); - - // Get the close button - const $closeButton = getTab(testFilePath).find(".tab-close"); - - // Create a spy for the FILE_CLOSE command - const executeOriginal = CommandManager.execute; - let fileCloseCalled = false; - let fileClosePathArg = null; - - CommandManager.execute = function (command, args) { - if (command === Commands.FILE_CLOSE) { - fileCloseCalled = true; - if (args && args.file) { - fileClosePathArg = args.file.fullPath; - } - } - return executeOriginal.apply(CommandManager, arguments); - }; - - // Click the close button - $closeButton.click(); - - // Cancel the save dialog if it appears - cancelSaveDialog(); - - // Wait for the tab to disappear - await awaitsFor( - function () { - return !tabExists(testFilePath); - }, - "Tab to disappear", - 1000 - ); - - // Restore the original execute function - CommandManager.execute = executeOriginal; - - // Verify the FILE_CLOSE command was called with the correct file - expect(fileCloseCalled).toBe(true); - expect(fileClosePathArg).toBe(testFilePath); - }); - }); - - describe("Context Menu", function () { - beforeEach(async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Open all three test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); - }); - - /** - * Helper function to get the context menu element - * @returns {jQuery} - The context menu element - */ - function getContextMenu() { - return $(".tabbar-context-menu"); - } - - it("should open context menu when right-clicking on a tab", async function () { - // Get the tab element - const $tab = getTab(testFilePath); - expect($tab.length).toBe(1); - - // Simulate a right-click (contextmenu) event on the tab - $tab.trigger("contextmenu", { - pageX: 100, - pageY: 100 - }); - - // Wait for the context menu to appear - await awaitsFor( - function () { - return getContextMenu().length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Verify the context menu is visible - expect(getContextMenu().length).toBe(1); - expect(getContextMenu().is(":visible")).toBe(true); - - // Clean up - close the context menu by clicking elsewhere - $("body").click(); - - // Wait for the context menu to disappear - await awaitsFor( - function () { - return getContextMenu().length === 0; - }, - "Context menu to disappear", - 1000 - ); - }); - - it("should close the tab when selecting 'Close Tab' from context menu", async function () { - // Get the tab element - const $tab = getTab(testFilePath); - - // Right-click on the tab to open context menu - $tab.trigger("contextmenu", { - pageX: 100, - pageY: 100 - }); - - // Wait for context menu to appear - await awaitsFor( - function () { - return getContextMenu().length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Find and click the "Close Tab" option - const $closeTabOption = getContextMenu() - .find("a.stylesheet-link") - .filter(function () { - return $(this).text().trim() === Strings.CLOSE_TAB; - }); - expect($closeTabOption.length).toBe(1); - $closeTabOption.click(); - - // Cancel the save dialog if it appears - cancelSaveDialog(); - - // Verify the tab is closed - await awaitsFor( - function () { - return !tabExists(testFilePath); - }, - "Tab to be closed", - 1000 - ); - }); - - it("should close tabs to the right when selecting 'Close tabs to the right' from context menu", async function () { - // Open all three test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); - - // Verify all three tabs exist - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(true); - expect(tabExists(testFilePath3)).toBe(true); - - // Get the first tab element - const $tab = getTab(testFilePath); - - // Right-click on the first tab to open context menu - $tab.trigger("contextmenu", { - pageX: 100, - pageY: 100 - }); - - // Wait for context menu to appear - await awaitsFor( - function () { - return getContextMenu().length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Find and click the "Close tabs to the right" option - const $closeTabsToRightOption = getContextMenu() - .find("a.stylesheet-link") - .filter(function () { - return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_RIGHT; - }); - expect($closeTabsToRightOption.length).toBe(1); - $closeTabsToRightOption.click(); - - // Cancel any save dialogs that might appear - cancelSaveDialog(); - - // Verify tabs to the right are closed - await awaitsFor( - function () { - return tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); - }, - "Tabs to the right to be closed", - 1000 - ); - - // Verify only the first tab remains - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(false); - expect(tabExists(testFilePath3)).toBe(false); - expect(getTabCount()).toBe(1); - }); - - it("should close tabs to the left when selecting 'Close tabs to the left' from context menu", async function () { - // Open all three test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); - - // Verify all three tabs exist - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(true); - expect(tabExists(testFilePath3)).toBe(true); - - // Get the third tab element - const $tab = getTab(testFilePath3); - - // Right-click on the third tab to open context menu - $tab.trigger("contextmenu", { - pageX: 100, - pageY: 100 - }); - - // Wait for context menu to appear - await awaitsFor( - function () { - return getContextMenu().length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Find and click the "Close tabs to the left" option - const $closeTabsToLeftOption = getContextMenu() - .find("a.stylesheet-link") - .filter(function () { - return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_LEFT; - }); - expect($closeTabsToLeftOption.length).toBe(1); - $closeTabsToLeftOption.click(); - - // Cancel any save dialogs that might appear - cancelSaveDialog(); - - // Verify tabs to the left are closed - await awaitsFor( - function () { - return !tabExists(testFilePath) && !tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "Tabs to the left to be closed", - 1000 - ); - - // Verify only the third tab remains - expect(tabExists(testFilePath)).toBe(false); - expect(tabExists(testFilePath2)).toBe(false); - expect(tabExists(testFilePath3)).toBe(true); - expect(getTabCount()).toBe(1); - }); - - it("should close saved tabs when selecting 'Close saved tabs' from context menu", async function () { - // Open all three test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); - - // Verify all three tabs exist - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(true); - expect(tabExists(testFilePath3)).toBe(true); - - // Make the second file dirty - const doc2 = DocumentManager.getOpenDocumentForPath(testFilePath2); - doc2.setText("// Modified content"); - - // Wait for the dirty indicator to appear - await awaitsFor( - function () { - return isTabDirty(testFilePath2); - }, - "Dirty indicator to appear", - 1000 - ); - - // Verify the second tab is dirty - expect(isTabDirty(testFilePath2)).toBe(true); - - // Get any tab element (we'll use the first) - const $tab = getTab(testFilePath); - - // Right-click on the tab to open context menu - $tab.trigger("contextmenu", { - pageX: 100, - pageY: 100 - }); - - // Wait for context menu to appear - await awaitsFor( - function () { - return getContextMenu().length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Find and click the "Close saved tabs" option - const $closeSavedTabsOption = getContextMenu() - .find("a.stylesheet-link") - .filter(function () { - return $(this).text().trim() === Strings.CLOSE_SAVED_TABS; - }); - expect($closeSavedTabsOption.length).toBe(1); - $closeSavedTabsOption.click(); - - // Cancel any save dialogs that might appear - cancelSaveDialog(); - - // Verify only the dirty tab remains - await awaitsFor( - function () { - return !tabExists(testFilePath) && tabExists(testFilePath2) && !tabExists(testFilePath3); - }, - "Saved tabs to be closed", - 1000 - ); - - expect(tabExists(testFilePath)).toBe(false); - expect(tabExists(testFilePath2)).toBe(true); - expect(tabExists(testFilePath3)).toBe(false); - expect(getTabCount()).toBe(1); - expect(isTabDirty(testFilePath2)).toBe(true); - - // Clean up - revert changes to the second file - doc2.setText("// Test file 2 for TabBar"); - await awaitsForDone( - CommandManager.execute(Commands.FILE_SAVE, { doc: doc2 }), - "Save file with original content" - ); - }); - - it("should close all tabs when selecting 'Close all tabs' from context menu", async function () { - // Open all three test files - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), - "Open first test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2 }), - "Open second test file" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath3 }), - "Open third test file" - ); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); - }, - "All tabs to appear", - 1000 - ); - - // Verify all three tabs exist - expect(tabExists(testFilePath)).toBe(true); - expect(tabExists(testFilePath2)).toBe(true); - expect(tabExists(testFilePath3)).toBe(true); - - // Get any tab element (we'll use the first) - const $tab = getTab(testFilePath); - - // Right-click on the tab to open context menu - $tab.trigger("contextmenu", { - pageX: 100, - pageY: 100 - }); - - // Wait for context menu to appear - await awaitsFor( - function () { - return getContextMenu().length > 0; - }, - "Context menu to appear", - 1000 - ); - - // Find and click the "Close all tabs" option - const $closeAllTabsOption = getContextMenu() - .find("a.stylesheet-link") - .filter(function () { - return $(this).text().trim() === Strings.CLOSE_ALL_TABS; - }); - expect($closeAllTabsOption.length).toBe(1); - $closeAllTabsOption.click(); - - // Cancel any save dialogs that might appear - cancelSaveDialog(); - - // Verify all tabs are closed - await awaitsFor( - function () { - return !tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); - }, - "All tabs to be closed", - 1000 - ); - - expect(tabExists(testFilePath)).toBe(false); - expect(tabExists(testFilePath2)).toBe(false); - expect(tabExists(testFilePath3)).toBe(false); - expect(getTabCount()).toBe(0); - }); - }); - - describe("Number of Tabs Preference", function () { - beforeEach(async function () { - // Enable the tab bar feature with default settings - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - }); - - afterEach(async function () { - // Reset preferences to default - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - }); - - it("should show all tabs when numberOfTabs is set to -1", async function () { - // Create several test files - const testFiles = []; - for (let i = 0; i < 10; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); - } - - // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } - - // Wait for all tabs to appear - await awaitsFor( - function () { - return getTabCount() >= testFiles.length; - }, - "All tabs to appear", - 1000 - ); - - // Verify all tabs are shown - expect(getTabCount()).toBe(testFiles.length); - - // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - cancelSaveDialog(); - await awaitsForDone(promise, `Close file ${filePath}`); - } - }); - - it("should limit the number of tabs shown when numberOfTabs is set to a positive value", async function () { - // Set the preference to show only 5 tabs - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 5 }); - - // Create several test files - const testFiles = []; - for (let i = 0; i < 10; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); - } - - // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } - - // Wait for tabs to appear - await awaitsFor( - function () { - return getTabCount() > 0; - }, - "Tabs to appear", - 1000 - ); - - // Verify only 5 tabs are shown - expect(getTabCount()).toBe(5); - - // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - cancelSaveDialog(); - await awaitsForDone(promise, `Close file ${filePath}`); - } - }); - - it("should hide the tab bar when numberOfTabs is set to 0", async function () { - // First open some files with the default setting - const testFiles = []; - for (let i = 0; i < 3; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/number-test-${i}.js`; - testFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Number test file ${i}`, FileSystem)); - } - - // Open all the test files - for (const filePath of testFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open file ${filePath}` - ); - } - - // Wait for tabs to appear - await awaitsFor( - function () { - return getTabCount() > 0; - }, - "Tabs to appear", - 1000 - ); - - // Verify tab bar is visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(true); - - // Now set numberOfTabs to 0 - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 0 }); - - // Wait for tab bar to disappear - await awaitsFor( - function () { - return !$("#phoenix-tab-bar").is(":visible"); - }, - "Tab bar to disappear", - 1000 - ); - - // Verify tab bar is hidden - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); - - // Clean up - close all the test files - for (const filePath of testFiles) { - const fileToClose = FileSystem.getFileForPath(filePath); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { file: fileToClose }); - cancelSaveDialog(); - await awaitsForDone(promise, `Close file ${filePath}`); - } - }); - - it("should apply numberOfTabs preference to both panes", async function () { - // Set up split pane layout - MainViewManager.setLayoutScheme(1, 2); - - // Set the preference to show only 3 tabs - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 3 }); - - // Create test files for first pane - const firstPaneFiles = []; - for (let i = 0; i < 5; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/first-pane-${i}.js`; - firstPaneFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// First pane file ${i}`, FileSystem)); - } - - // Create test files for second pane - const secondPaneFiles = []; - for (let i = 0; i < 5; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/second-pane-${i}.js`; - secondPaneFiles.push(filePath); - await jsPromise(SpecRunnerUtils.createTextFile(filePath, `// Second pane file ${i}`, FileSystem)); - } - - // Open files in first pane - for (const filePath of firstPaneFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "first-pane" }), - `Open file ${filePath} in first pane` - ); - } - - // Open files in second pane - for (const filePath of secondPaneFiles) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: "second-pane" }), - `Open file ${filePath} in second pane` - ); - } - - // Wait for both tab bars to appear - await awaitsFor( - function () { - return $("#phoenix-tab-bar").is(":visible") && $("#phoenix-tab-bar-2").is(":visible"); - }, - "Both tab bars to appear", - 1000 - ); - - // Verify each pane shows only 3 tabs - expect($("#phoenix-tab-bar").find(".tab").length).toBe(3); - expect($("#phoenix-tab-bar-2").find(".tab").length).toBe(3); - - // Change preference to show all tabs - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Wait for all tabs to appear - await awaitsFor( - function () { - return ( - $("#phoenix-tab-bar").find(".tab").length === 5 && - $("#phoenix-tab-bar-2").find(".tab").length === 5 - ); - }, - "All tabs to appear in both panes", - 1000 - ); - - // Verify all tabs are shown in both panes - expect($("#phoenix-tab-bar").find(".tab").length).toBe(5); - expect($("#phoenix-tab-bar-2").find(".tab").length).toBe(5); - - // Change preference to hide tab bars - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: 0 }); - - // Wait for both tab bars to disappear - await awaitsFor( - function () { - return !$("#phoenix-tab-bar").is(":visible") && !$("#phoenix-tab-bar-2").is(":visible"); - }, - "Both tab bars to disappear", - 1000 - ); - - // Verify both tab bars are hidden - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); - expect($("#phoenix-tab-bar-2").is(":visible")).toBe(false); - - // Clean up - close all files and reset to single pane - await testWindow.closeAllFiles(); - MainViewManager.setLayoutScheme(1, 1); - }); - }); - - describe("Split Panes", function () { - beforeEach(async function () { - // Enable the tab bar feature - PreferencesManager.set("tabBar.options", { showTabBar: true, numberOfTabs: -1 }); - - // Close all files to start with a clean state - await testWindow.closeAllFiles(); - - // Set up a horizontal split view (two columns) - MainViewManager.setLayoutScheme(1, 2); - }); - - afterEach(async function () { - // Reset to single pane layout - MainViewManager.setLayoutScheme(1, 1); - }); - - it("should show tab bars in both panes when files are open in both", async function () { - // Open a file in the first pane - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), - "Open file in first pane" - ); - - // Open a different file in the second pane - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), - "Open file in second pane" - ); - - // Wait for both tab bars to appear - await awaitsFor( - function () { - return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); - }, - "Both tab bars to appear", - 1000 - ); - - // Verify both tab bars are visible - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(true); - - // Verify each pane has the correct tab - expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); - expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(true); - expect(getPaneTabCount("first-pane")).toBe(1); - expect(getPaneTabCount("second-pane")).toBe(1); - }); - - it("should hide tab bar in a pane with no files", async function () { - // Open a file in the first pane only - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), - "Open file in first pane" - ); - - // Wait for the first tab bar to appear - await awaitsFor( - function () { - return isTabBarVisible("first-pane"); - }, - "First tab bar to appear", - 1000 - ); - - // Verify first tab bar is visible and second is not - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(false); - - // Verify the first pane has the correct tab - expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); - expect(getPaneTabCount("first-pane")).toBe(1); - }); - - it("should update tab bars when moving files between panes", async function () { - // Open a file in the first pane - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), - "Open file in first pane" - ); - - // Wait for the first tab bar to appear - await awaitsFor( - function () { - return isTabBarVisible("first-pane"); - }, - "First tab bar to appear", - 1000 - ); - - // Verify first tab bar is visible and second is not - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(false); - - // Move the file to the second pane - const fileObj = FileSystem.getFileForPath(testFilePath); - MainViewManager.addToWorkingSet("second-pane", fileObj); - - // Remove from first pane - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: "first-pane" }), - "Close file in first pane" - ); - - // Wait for the tab to appear in the second pane and disappear from the first - await awaitsFor( - function () { - return ( - !tabExistsInPane(testFilePath, "first-pane") && tabExistsInPane(testFilePath, "second-pane") - ); - }, - "Tab to move to second pane", - 1000 - ); - - // Verify the tab bars visibility has updated - expect(isTabBarVisible("first-pane")).toBe(false); - expect(isTabBarVisible("second-pane")).toBe(true); - - // Verify the tab is now in the second pane - expect(tabExistsInPane(testFilePath, "first-pane")).toBe(false); - expect(tabExistsInPane(testFilePath, "second-pane")).toBe(true); - expect(getPaneTabCount("first-pane")).toBe(0); - expect(getPaneTabCount("second-pane")).toBe(1); - }); - - it("should hide tab bar when closing all files in a pane", async function () { - // Open files in both panes - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), - "Open file in first pane" - ); - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), - "Open file in second pane" - ); - - // Wait for both tab bars to appear - await awaitsFor( - function () { - return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); - }, - "Both tab bars to appear", - 1000 - ); - - // Verify both tab bars are visible - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(true); - - // Close the file in the second pane - const fileToClose = FileSystem.getFileForPath(testFilePath2); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { - file: fileToClose, - paneId: "second-pane" - }); - - // Cancel the save dialog if it appears - cancelSaveDialog(); - - await awaitsForDone(promise, "Close file in second pane"); - - // Wait for the second tab bar to disappear - await awaitsFor( - function () { - return !isTabBarVisible("second-pane"); - }, - "Second tab bar to disappear", - 1000 - ); - - // Verify first tab bar is still visible but second is not - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(false); - - // Verify the tabs are in the correct panes - expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); - expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(false); - expect(getPaneTabCount("first-pane")).toBe(1); - expect(getPaneTabCount("second-pane")).toBe(0); - }); - - it("should work correctly with vertical split layout", async function () { - // Change to vertical split layout (2 rows, 1 column) - MainViewManager.setLayoutScheme(2, 1); - - // Open a file in the first pane - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath, paneId: "first-pane" }), - "Open file in first pane" - ); - - // Open a different file in the second pane - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath2, paneId: "second-pane" }), - "Open file in second pane" - ); - - // Wait for both tab bars to appear - await awaitsFor( - function () { - return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); - }, - "Both tab bars to appear", - 1000 - ); - - // Verify both tab bars are visible - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(true); - - // Verify each pane has the correct tab - expect(tabExistsInPane(testFilePath, "first-pane")).toBe(true); - expect(tabExistsInPane(testFilePath2, "second-pane")).toBe(true); - expect(getPaneTabCount("first-pane")).toBe(1); - expect(getPaneTabCount("second-pane")).toBe(1); - - // Close the file in the second pane - const fileToClose = FileSystem.getFileForPath(testFilePath2); - const promise = CommandManager.execute(Commands.FILE_CLOSE, { - file: fileToClose, - paneId: "second-pane" - }); - - // Cancel the save dialog if it appears - cancelSaveDialog(); - - await awaitsForDone(promise, "Close file in second pane"); - - // Wait for the second tab bar to disappear - await awaitsFor( - function () { - return !isTabBarVisible("second-pane"); - }, - "Second tab bar to disappear", - 1000 - ); - - // Verify first tab bar is still visible but second is not - expect(isTabBarVisible("first-pane")).toBe(true); - expect(isTabBarVisible("second-pane")).toBe(false); - - // Reset to horizontal split for other tests - MainViewManager.setLayoutScheme(1, 2); - }); - }); - - describe("Tab Bar Scrolling", function () { - let longTestFilePaths = []; - - beforeEach(async function () { - // Create multiple test files to ensure scrolling is needed - longTestFilePaths = []; - for (let i = 1; i <= 15; i++) { - const filePath = SpecRunnerUtils.getTempDirectory() + `/scroll-test-file-${i}.js`; - longTestFilePaths.push(filePath); - await jsPromise( - SpecRunnerUtils.createTextFile(filePath, `// Test file ${i} for scrolling`, FileSystem) - ); - } - - // Open all files to create many tabs - for (let filePath of longTestFilePaths) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }), - `Open ${filePath}` - ); - } - - // Wait for tabs to be rendered - await awaitsFor( - function () { - return getTabCount() >= 15; - }, - "All tabs to be created", - 3000 - ); - }); - - afterEach(async function () { - // Close all test files - for (let filePath of longTestFilePaths) { - const fileObj = FileSystem.getFileForPath(filePath); - try { - await awaitsForDone( - CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj }), - `Close ${filePath}` - ); - } catch (e) { - // Ignore errors if file is already closed - } - } - }); - - it("should scroll tab bar horizontally when mouse wheel is scrolled", function () { - const $tabBar = $("#phoenix-tab-bar"); - expect($tabBar.length).toBe(1); - - // Get initial scroll position - const initialScrollLeft = $tabBar.scrollLeft(); - - // Create a wheel event for scrolling down (should scroll right) - const wheelEventDown = $.Event("wheel"); - wheelEventDown.originalEvent = { deltaY: 100 }; // Positive deltaY = scroll down/right - - // Trigger the wheel event - $tabBar.trigger(wheelEventDown); - - // Check that scroll position has changed to the right - const scrollAfterDown = $tabBar.scrollLeft(); - expect(scrollAfterDown).toBeGreaterThan(initialScrollLeft); - // Verify the scroll amount is proportional to deltaY (implementation multiplies by 2.5) - expect(scrollAfterDown - initialScrollLeft).toBeCloseTo(100 * 2.5, 0); - - // Create a wheel event for scrolling up (should scroll left) - const wheelEventUp = $.Event("wheel"); - wheelEventUp.originalEvent = { deltaY: -100 }; // Negative deltaY = scroll up/left - - // Trigger the wheel event - $tabBar.trigger(wheelEventUp); - - // Check that scroll position has moved left from the previous position - const scrollAfterUp = $tabBar.scrollLeft(); - expect(scrollAfterUp).toBeLessThan(scrollAfterDown); - // Verify the scroll amount is proportional to deltaY - expect(scrollAfterDown - scrollAfterUp).toBeCloseTo(100 * 2.5, 0); - }); - - it("should scroll tab bar with trackpad scrolling", function () { - const $tabBar = $("#phoenix-tab-bar"); - expect($tabBar.length).toBe(1); - - // Get initial scroll position - const initialScrollLeft = $tabBar.scrollLeft(); - - // Create a wheel event simulating trackpad scrolling (smaller deltaY values) - const trackpadEvent = $.Event("wheel"); - trackpadEvent.originalEvent = { deltaY: 25 }; // Smaller value typical of trackpad - - // Trigger the trackpad scrolling event multiple times - for (let i = 0; i < 4; i++) { - $tabBar.trigger(trackpadEvent); - } - - // Check that scroll position has changed - const scrollAfterTrackpad = $tabBar.scrollLeft(); - expect(scrollAfterTrackpad).toBeGreaterThan(initialScrollLeft); - // Verify the total scroll amount after multiple small scrolls - // 4 scrolls of 25 * 2.5 = 250 pixels total - expect(scrollAfterTrackpad - initialScrollLeft).toBeCloseTo(4 * 25 * 2.5, 0); - }); - - it("should scroll second pane tab bar when it exists", async function () { - // Create the second pane first - MainViewManager.setLayoutScheme(1, 2); - - // Wait for the layout to be created - await awaitsFor( - function () { - return MainViewManager.getPaneIdList().length === 2; - }, - "Second pane to be created", - 1000 - ); - - // Open a file in the second pane to create the second tab bar - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { - fullPath: longTestFilePaths[0], - paneId: "second-pane" - }), - "Open file in second pane" - ); - - // Wait for second tab bar to appear - await awaitsFor( - function () { - return $("#phoenix-tab-bar-2").length > 0; - }, - "Second tab bar to appear", - 1000 - ); - - const $tabBar2 = $("#phoenix-tab-bar-2"); - expect($tabBar2.length).toBe(1); - - // Open multiple files in second pane to enable scrolling - for (let i = 1; i < 8; i++) { - await awaitsForDone( - CommandManager.execute(Commands.FILE_OPEN, { - fullPath: longTestFilePaths[i], - paneId: "second-pane" - }), - `Open file ${i} in second pane` - ); - } - - // Wait for tabs to be rendered in second pane - await awaitsFor( - function () { - return $tabBar2.find(".tab").length >= 8; - }, - "Tabs to be created in second pane", - 2000 - ); - - // Get initial scroll position of second tab bar - const initialScrollLeft = $tabBar2.scrollLeft(); - - // Create a wheel event for scrolling - const wheelEvent = $.Event("wheel"); - wheelEvent.originalEvent = { deltaY: 150 }; - - // Trigger the wheel event on second tab bar - $tabBar2.trigger(wheelEvent); - - // Check that scroll position has changed - const scrollAfterWheel = $tabBar2.scrollLeft(); - expect(scrollAfterWheel).toBeGreaterThan(initialScrollLeft); - // Verify the scroll amount is proportional to deltaY - expect(scrollAfterWheel - initialScrollLeft).toBeCloseTo(150 * 2.5, 0); - - // Reset layout scheme back to single pane - MainViewManager.setLayoutScheme(1, 1); - }); - - it("should calculate correct scroll amount based on deltaY", function () { - const $tabBar = $("#phoenix-tab-bar"); - expect($tabBar.length).toBe(1); - - // Ensure the tab bar is scrollable by checking if scrollWidth > clientWidth - if ($tabBar[0].scrollWidth <= $tabBar[0].clientWidth) { - // Skip test if tab bar is not scrollable - return; - } - - // Set initial scroll position to middle to allow scrolling in both directions - const maxScroll = $tabBar[0].scrollWidth - $tabBar[0].clientWidth; - const midScroll = Math.floor(maxScroll / 2); - $tabBar.scrollLeft(midScroll); - - // Test positive deltaY (scroll right) - const initialScrollLeft = $tabBar.scrollLeft(); - const wheelEventRight = $.Event("wheel"); - wheelEventRight.originalEvent = { deltaY: 40 }; - $tabBar.trigger(wheelEventRight); - - const scrollAfterRight = $tabBar.scrollLeft(); - expect(scrollAfterRight).toBeGreaterThan(initialScrollLeft); - expect(scrollAfterRight - initialScrollLeft).toBeCloseTo(40 * 2.5, 0); - - // Reset and test negative deltaY (scroll left) - $tabBar.scrollLeft(midScroll); - const wheelEventLeft = $.Event("wheel"); - wheelEventLeft.originalEvent = { deltaY: -40 }; - $tabBar.trigger(wheelEventLeft); - - const scrollAfterLeft = $tabBar.scrollLeft(); - expect(scrollAfterLeft).toBeLessThan(midScroll); - expect(midScroll - scrollAfterLeft).toBeCloseTo(40 * 2.5, 0); - }); - - it("should not scroll beyond the scrollable bounds", function () { - const $tabBar = $("#phoenix-tab-bar"); - expect($tabBar.length).toBe(1); - - // Get the maximum scrollable width - const maxScrollLeft = $tabBar[0].scrollWidth - $tabBar[0].clientWidth; - - // Scroll far to the right - $tabBar.scrollLeft(maxScrollLeft + 1000); // Try to scroll beyond max - - // Create a wheel event to scroll further right - const wheelEventRight = $.Event("wheel"); - wheelEventRight.originalEvent = { deltaY: 500 }; // Large scroll right - $tabBar.trigger(wheelEventRight); - - // Should not exceed maximum scroll (a small floating point tolerance) - expect($tabBar.scrollLeft()).toBeLessThanOrEqual(maxScrollLeft + 1); - - // Scroll far to the left - $tabBar.scrollLeft(-1000); // Try to scroll beyond minimum - - // Create a wheel event to scroll further left - const wheelEventLeft = $.Event("wheel"); - wheelEventLeft.originalEvent = { deltaY: -500 }; // Large scroll left - $tabBar.trigger(wheelEventLeft); - - // Should not go below 0 (a small floating point tolerance) - expect($tabBar.scrollLeft()).toBeGreaterThanOrEqual(-1); - }); - }); - }); -}); From 722d0d19417c1c3089b6282f60b5fa3f3fde85f7 Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 20:48:52 +0530 Subject: [PATCH 25/26] refactor: remove awaitsFor timer as redundant --- test/spec/Extn-Tabbar-integ-test.js | 286 ++++++++++------------------ 1 file changed, 96 insertions(+), 190 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 4d76193ec4..be6521d034 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -90,8 +90,7 @@ define(function (require, exports, module) { function () { return getPaneTabCount(paneId) >= filePaths.length; }, - `All tabs to appear in ${paneId}`, - 1000 + `All tabs to appear in ${paneId}` ); } else if (filePaths.length === 1) { // Wait for a single tab to appear @@ -99,8 +98,7 @@ define(function (require, exports, module) { function () { return tabExists(filePaths[0]); }, - `Tab for ${filePaths[0]} to appear`, - 1000 + `Tab for ${filePaths[0]} to appear` ); } else { // Wait for multiple tabs to appear @@ -108,8 +106,7 @@ define(function (require, exports, module) { function () { return getTabCount() >= filePaths.length && filePaths.every((path) => tabExists(path)); }, - "All tabs to appear", - 1000 + "All tabs to appear" ); } } @@ -285,7 +282,7 @@ define(function (require, exports, module) { CommandManager.execute(Commands.FILE_OPEN, { fullPath: testFilePath }), "Open test file" ); - }, 30000); + }, 5000); afterAll(async function () { // Close all files without prompting to save @@ -294,7 +291,7 @@ define(function (require, exports, module) { testWindow = null; await SpecRunnerUtils.closeTestWindow(); await SpecRunnerUtils.removeTempDirectory(); - }, 30000); + }, 5000); /** * Helper function to check if a tab for a specific file exists in the tab bar @@ -475,8 +472,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").is(":visible"); }, - "Tab bar to become visible", - 1000 + "Tab bar to become visible" ); // Verify the tab bar is visible @@ -492,8 +488,7 @@ define(function (require, exports, module) { function () { return !$("#phoenix-tab-bar").is(":visible"); }, - "Tab bar to become hidden", - 1000 + "Tab bar to become hidden" ); // Verify the tab bar is not visible @@ -509,8 +504,7 @@ define(function (require, exports, module) { function () { return !$("#working-set-list-container").hasClass("working-set-hidden"); }, - "Working set to become visible", - 1000 + "Working set to become visible" ); // Verify the working set is visible @@ -526,8 +520,7 @@ define(function (require, exports, module) { function () { return $("#working-set-list-container").hasClass("working-set-hidden"); }, - "Working set to become hidden", - 1000 + "Working set to become hidden" ); // Verify the working set is not visible @@ -552,8 +545,7 @@ define(function (require, exports, module) { function () { return $(".dropdown-menu:visible").length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Verify the menu contains the expected options @@ -575,8 +567,7 @@ define(function (require, exports, module) { function () { return !$("#working-set-list-container").hasClass("working-set-hidden"); }, - "Working set to become visible", - 1000 + "Working set to become visible" ); // Click the configure working set button @@ -588,8 +579,7 @@ define(function (require, exports, module) { function () { return $(".dropdown-menu:visible").length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Click the "Show working set" option @@ -602,8 +592,7 @@ define(function (require, exports, module) { function () { return $("#working-set-list-container").hasClass("working-set-hidden"); }, - "Working set to become hidden", - 1000 + "Working set to become hidden" ); // Verify the working set is hidden @@ -617,8 +606,7 @@ define(function (require, exports, module) { function () { return $(".dropdown-menu:visible").length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Click the "Show working set" option again @@ -631,8 +619,7 @@ define(function (require, exports, module) { function () { return !$("#working-set-list-container").hasClass("working-set-hidden"); }, - "Working set to become visible", - 1000 + "Working set to become visible" ); // Verify the working set is visible @@ -646,8 +633,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").is(":visible"); }, - "Tab bar to become visible", - 1000 + "Tab bar to become visible" ); // Click the configure working set button @@ -659,8 +645,7 @@ define(function (require, exports, module) { function () { return $(".dropdown-menu:visible").length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Click the "Show file tab bar" option @@ -673,8 +658,7 @@ define(function (require, exports, module) { function () { return !$("#phoenix-tab-bar").is(":visible"); }, - "Tab bar to become hidden", - 1000 + "Tab bar to become hidden" ); // Verify the tab bar is hidden @@ -688,8 +672,7 @@ define(function (require, exports, module) { function () { return $(".dropdown-menu:visible").length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Click the "Show file tab bar" option again @@ -702,8 +685,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").is(":visible"); }, - "Tab bar to become visible", - 1000 + "Tab bar to become visible" ); // Verify the tab bar is visible @@ -722,8 +704,7 @@ define(function (require, exports, module) { function () { return MainViewManager.getPaneCount() === 1 && getTabCount() === 0; }, - "Cleanup to complete with single pane and no tabs", - 1000 + "Cleanup to complete with single pane and no tabs" ); }); @@ -759,8 +740,7 @@ define(function (require, exports, module) { currentWorkingSet[1].fullPath === testFiles[0] ); }, - "Working set to update after drag and drop", - 1000 + "Working set to update after drag and drop" ); // Verify the new tab order @@ -806,8 +786,7 @@ define(function (require, exports, module) { currentWorkingSet[1].fullPath === testFiles[2] ); }, - "Working set to update after drag and drop", - 1000 + "Working set to update after drag and drop" ); // Verify the new tab order @@ -901,8 +880,7 @@ define(function (require, exports, module) { tabExistsInPane(firstPaneFiles[0], "second-pane") ); }, - "Tab to move from first pane to second pane", - 1000 + "Tab to move from first pane to second pane" ); // Verify the tab counts after the drag and drop @@ -933,8 +911,7 @@ define(function (require, exports, module) { function () { return MainViewManager.getPaneCount() === 2; }, - "Layout to settle with two panes", - 1000 + "Layout to settle with two panes" ); // Verify both panes are empty @@ -972,8 +949,7 @@ define(function (require, exports, module) { function () { return getPaneTabCount("second-pane") === 0; }, - "Second pane to be empty after cleanup", - 2000 + "Second pane to be empty after cleanup" ); } @@ -1043,8 +1019,7 @@ define(function (require, exports, module) { isTabBarVisible("second-pane") ); }, - "Tab to move from first pane to second pane and tab bar to appear", - 2000 + "Tab to move from first pane to second pane and tab bar to appear" ); // Verify the tab counts after the drag and drop @@ -1074,8 +1049,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").length > 0 && getTabCount() === 0; }, - "Tab bar to update with no tabs", - 1000 + "Tab bar to update with no tabs" ); }); @@ -1125,8 +1099,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath2); }, - "Tab for second file to disappear", - 1000 + "Tab for second file to disappear" ); // Verify the second tab is removed @@ -1143,8 +1116,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath); }, - "Tab for first file to disappear", - 1000 + "Tab for first file to disappear" ); // Verify the first tab is removed @@ -1161,8 +1133,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath3); }, - "Tab for third file to disappear", - 1000 + "Tab for third file to disappear" ); // Verify all tabs are removed @@ -1198,8 +1169,7 @@ define(function (require, exports, module) { function () { return isTabActive(filePath); }, - `${description} to become active`, - 1000 + `${description} to become active` ); // Verify this tab is active and others are not @@ -1223,8 +1193,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").length > 0 && activeFile && isTabActive(activeFile.fullPath); }, - "Tab bar to be recreated and show active file", - 1000 + "Tab bar to be recreated and show active file" ); // Verify the tab for the active file is active @@ -1241,8 +1210,7 @@ define(function (require, exports, module) { function () { return newActiveFile && isTabActive(newActiveFile.fullPath); }, - "Tab bar to update with new active file", - 1000 + "Tab bar to update with new active file" ); // Verify the tab for the new active file is active @@ -1266,8 +1234,7 @@ define(function (require, exports, module) { isTabActive(filePath) && MainViewManager.getCurrentlyViewedFile().fullPath === filePath ); }, - `${description} to become active after tab click`, - 1000 + `${description} to become active after tab click` ); // Verify this tab is active and others are not @@ -1284,8 +1251,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").length > 0 && getTabCount() === 3; }, - "Tab bar to be properly loaded with all tabs", - 1000 + "Tab bar to be properly loaded with all tabs" ); // Wait for the third file to become active @@ -1297,8 +1263,7 @@ define(function (require, exports, module) { MainViewManager.getCurrentlyViewedFile().fullPath === testFilePath3 ); }, - "Third file to become active", - 2000 + "Third file to become active" ); // Initially, verify the third file is active (last opened) @@ -1339,8 +1304,7 @@ define(function (require, exports, module) { function () { return isOverflowButtonVisible(); }, - "Overflow button to appear", - 1000 + "Overflow button to appear" ); // Verify the overflow button is visible @@ -1380,8 +1344,7 @@ define(function (require, exports, module) { function () { return isOverflowButtonVisible(); }, - "Overflow button to appear", - 1000 + "Overflow button to appear" ); // Get the list of hidden tabs @@ -1396,8 +1359,7 @@ define(function (require, exports, module) { function () { return getOverflowDropdown().length > 0; }, - "Overflow dropdown to appear", - 1000 + "Overflow dropdown to appear" ); // Verify the dropdown is visible @@ -1421,8 +1383,7 @@ define(function (require, exports, module) { function () { return getOverflowDropdown().length === 0; }, - "Overflow dropdown to disappear", - 1000 + "Overflow dropdown to disappear" ); // Clean up - close all the test files @@ -1444,8 +1405,7 @@ define(function (require, exports, module) { function () { return isOverflowButtonVisible(); }, - "Overflow button to appear", - 1000 + "Overflow button to appear" ); // Get the list of hidden tabs @@ -1463,8 +1423,7 @@ define(function (require, exports, module) { function () { return getOverflowDropdown().length > 0; }, - "Overflow dropdown to appear", - 1000 + "Overflow dropdown to appear" ); // Get the dropdown item for the test file @@ -1482,8 +1441,7 @@ define(function (require, exports, module) { MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile ); }, - "Hidden file to become active after dropdown item click", - 1000 + "Hidden file to become active after dropdown item click" ); // Verify the file is active @@ -1495,8 +1453,7 @@ define(function (require, exports, module) { function () { return isTabVisible(testHiddenFile); }, - "Tab to become visible after dropdown item click", - 1000 + "Tab to become visible after dropdown item click" ); expect(isTabVisible(testHiddenFile)).toBe(true); @@ -1520,8 +1477,7 @@ define(function (require, exports, module) { function () { return isOverflowButtonVisible(); }, - "Overflow button to appear", - 1000 + "Overflow button to appear" ); // Get the list of hidden tabs @@ -1548,8 +1504,7 @@ define(function (require, exports, module) { MainViewManager.getCurrentlyViewedFile().fullPath === testHiddenFile ); }, - "Hidden file to become active after selection from working set", - 1000 + "Hidden file to become active after selection from working set" ); // Verify the file is active @@ -1561,8 +1516,7 @@ define(function (require, exports, module) { function () { return isTabVisible(testHiddenFile); }, - "Tab to become visible after selection from working set", - 1000 + "Tab to become visible after selection from working set" ); expect(isTabVisible(testHiddenFile)).toBe(true); @@ -1593,8 +1547,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath); }, - "Tab to appear", - 1000 + "Tab to appear" ); // Get the filename from the path @@ -1616,8 +1569,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath); }, - "Tab to appear", - 1000 + "Tab to appear" ); // Verify the tab has a file icon @@ -1636,8 +1588,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath); }, - "Tab to appear", - 1000 + "Tab to appear" ); // Initially, the file should not be dirty @@ -1652,8 +1603,7 @@ define(function (require, exports, module) { function () { return isTabDirty(testFilePath); }, - "Dirty indicator to appear", - 1000 + "Dirty indicator to appear" ); // Verify the tab has a dirty indicator @@ -1667,8 +1617,7 @@ define(function (require, exports, module) { function () { return !isTabDirty(testFilePath); }, - "Dirty indicator to disappear", - 1000 + "Dirty indicator to disappear" ); // Verify the tab no longer has a dirty indicator @@ -1701,8 +1650,7 @@ define(function (require, exports, module) { function () { return tabExists(duplicateFile1) && tabExists(duplicateFile2); }, - "Both duplicate tabs to appear", - 1000 + "Both duplicate tabs to appear" ); // Verify both tabs have directory names @@ -1726,8 +1674,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath); }, - "Tab to appear", - 1000 + "Tab to appear" ); // Verify the tooltip contains the full path @@ -1786,8 +1733,7 @@ define(function (require, exports, module) { function () { return hasGitStatus(testFilePath) && hasGitStatus(testFilePath2); }, - "Tabs to update with Git status", - 1000 + "Tabs to update with Git status" ); // Verify the first file has the git-new class @@ -1820,8 +1766,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath); }, - "Tab to appear", - 1000 + "Tab to appear" ); // Verify the tab has a close button @@ -1844,8 +1789,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath); }, - "Tab to appear", - 1000 + "Tab to appear" ); // Get the close button @@ -1877,8 +1821,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath); }, - "Tab to disappear", - 1000 + "Tab to disappear" ); // Restore the original execute function @@ -1917,8 +1860,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); }, - "All tabs to appear", - 1000 + "All tabs to appear" ); }); @@ -1946,8 +1888,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Verify the context menu is visible @@ -1962,8 +1903,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length === 0; }, - "Context menu to disappear", - 1000 + "Context menu to disappear" ); }); @@ -1982,8 +1922,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Find and click the "Close Tab" option @@ -2003,8 +1942,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath); }, - "Tab to be closed", - 1000 + "Tab to be closed" ); }); @@ -2028,8 +1966,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); }, - "All tabs to appear", - 1000 + "All tabs to appear" ); // Verify all three tabs exist @@ -2051,8 +1988,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Find and click the "Close tabs to the right" option @@ -2072,8 +2008,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); }, - "Tabs to the right to be closed", - 1000 + "Tabs to the right to be closed" ); // Verify only the first tab remains @@ -2103,8 +2038,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); }, - "All tabs to appear", - 1000 + "All tabs to appear" ); // Verify all three tabs exist @@ -2126,8 +2060,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Find and click the "Close tabs to the left" option @@ -2147,8 +2080,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath) && !tabExists(testFilePath2) && tabExists(testFilePath3); }, - "Tabs to the left to be closed", - 1000 + "Tabs to the left to be closed" ); // Verify only the third tab remains @@ -2178,8 +2110,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); }, - "All tabs to appear", - 1000 + "All tabs to appear" ); // Verify all three tabs exist @@ -2196,8 +2127,7 @@ define(function (require, exports, module) { function () { return isTabDirty(testFilePath2); }, - "Dirty indicator to appear", - 1000 + "Dirty indicator to appear" ); // Verify the second tab is dirty @@ -2217,8 +2147,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Find and click the "Close saved tabs" option @@ -2238,8 +2167,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath) && tabExists(testFilePath2) && !tabExists(testFilePath3); }, - "Saved tabs to be closed", - 1000 + "Saved tabs to be closed" ); expect(tabExists(testFilePath)).toBe(false); @@ -2276,8 +2204,7 @@ define(function (require, exports, module) { function () { return tabExists(testFilePath) && tabExists(testFilePath2) && tabExists(testFilePath3); }, - "All tabs to appear", - 1000 + "All tabs to appear" ); // Verify all three tabs exist @@ -2299,8 +2226,7 @@ define(function (require, exports, module) { function () { return getContextMenu().length > 0; }, - "Context menu to appear", - 1000 + "Context menu to appear" ); // Find and click the "Close all tabs" option @@ -2320,8 +2246,7 @@ define(function (require, exports, module) { function () { return !tabExists(testFilePath) && !tabExists(testFilePath2) && !tabExists(testFilePath3); }, - "All tabs to be closed", - 1000 + "All tabs to be closed" ); expect(tabExists(testFilePath)).toBe(false); @@ -2367,8 +2292,7 @@ define(function (require, exports, module) { function () { return getTabCount() >= testFiles.length; }, - "All tabs to appear", - 1000 + "All tabs to appear" ); // Verify all tabs are shown @@ -2408,8 +2332,7 @@ define(function (require, exports, module) { function () { return getTabCount() > 0; }, - "Tabs to appear", - 1000 + "Tabs to appear" ); // Verify only 5 tabs are shown @@ -2446,8 +2369,7 @@ define(function (require, exports, module) { function () { return getTabCount() > 0; }, - "Tabs to appear", - 1000 + "Tabs to appear" ); // Verify tab bar is visible @@ -2461,8 +2383,7 @@ define(function (require, exports, module) { function () { return !$("#phoenix-tab-bar").is(":visible"); }, - "Tab bar to disappear", - 1000 + "Tab bar to disappear" ); // Verify tab bar is hidden @@ -2521,8 +2442,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar").is(":visible") && $("#phoenix-tab-bar-2").is(":visible"); }, - "Both tab bars to appear", - 1000 + "Both tab bars to appear" ); // Verify each pane shows only 3 tabs @@ -2540,8 +2460,7 @@ define(function (require, exports, module) { $("#phoenix-tab-bar-2").find(".tab").length === 5 ); }, - "All tabs to appear in both panes", - 1000 + "All tabs to appear in both panes" ); // Verify all tabs are shown in both panes @@ -2556,8 +2475,7 @@ define(function (require, exports, module) { function () { return !$("#phoenix-tab-bar").is(":visible") && !$("#phoenix-tab-bar-2").is(":visible"); }, - "Both tab bars to disappear", - 1000 + "Both tab bars to disappear" ); // Verify both tab bars are hidden @@ -2605,8 +2523,7 @@ define(function (require, exports, module) { function () { return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); }, - "Both tab bars to appear", - 1000 + "Both tab bars to appear" ); // Verify both tab bars are visible @@ -2632,8 +2549,7 @@ define(function (require, exports, module) { function () { return isTabBarVisible("first-pane"); }, - "First tab bar to appear", - 1000 + "First tab bar to appear" ); // Verify first tab bar is visible and second is not @@ -2657,8 +2573,7 @@ define(function (require, exports, module) { function () { return isTabBarVisible("first-pane"); }, - "First tab bar to appear", - 1000 + "First tab bar to appear" ); // Verify first tab bar is visible and second is not @@ -2682,8 +2597,7 @@ define(function (require, exports, module) { !tabExistsInPane(testFilePath, "first-pane") && tabExistsInPane(testFilePath, "second-pane") ); }, - "Tab to move to second pane", - 1000 + "Tab to move to second pane" ); // Verify the tab bars visibility has updated @@ -2713,8 +2627,7 @@ define(function (require, exports, module) { function () { return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); }, - "Both tab bars to appear", - 1000 + "Both tab bars to appear" ); // Verify both tab bars are visible @@ -2738,8 +2651,7 @@ define(function (require, exports, module) { function () { return !isTabBarVisible("second-pane"); }, - "Second tab bar to disappear", - 1000 + "Second tab bar to disappear" ); // Verify first tab bar is still visible but second is not @@ -2774,8 +2686,7 @@ define(function (require, exports, module) { function () { return isTabBarVisible("first-pane") && isTabBarVisible("second-pane"); }, - "Both tab bars to appear", - 1000 + "Both tab bars to appear" ); // Verify both tab bars are visible @@ -2805,8 +2716,7 @@ define(function (require, exports, module) { function () { return !isTabBarVisible("second-pane"); }, - "Second tab bar to disappear", - 1000 + "Second tab bar to disappear" ); // Verify first tab bar is still visible but second is not @@ -2845,8 +2755,7 @@ define(function (require, exports, module) { function () { return getTabCount() >= 15; }, - "All tabs to be created", - 3000 + "All tabs to be created" ); }); @@ -2929,8 +2838,7 @@ define(function (require, exports, module) { function () { return MainViewManager.getPaneIdList().length === 2; }, - "Second pane to be created", - 1000 + "Second pane to be created" ); // Open a file in the second pane to create the second tab bar @@ -2947,8 +2855,7 @@ define(function (require, exports, module) { function () { return $("#phoenix-tab-bar-2").length > 0; }, - "Second tab bar to appear", - 1000 + "Second tab bar to appear" ); const $tabBar2 = $("#phoenix-tab-bar-2"); @@ -2970,8 +2877,7 @@ define(function (require, exports, module) { function () { return $tabBar2.find(".tab").length >= 8; }, - "Tabs to be created in second pane", - 2000 + "Tabs to be created in second pane" ); // Get initial scroll position of second tab bar From 63871feae0cc544af2115e200fdde7b1ce2d0c0c Mon Sep 17 00:00:00 2001 From: Pluto Date: Wed, 9 Jul 2025 21:06:20 +0530 Subject: [PATCH 26/26] fix: remove redundant checks --- test/spec/Extn-Tabbar-integ-test.js | 36 ----------------------------- 1 file changed, 36 deletions(-) diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index be6521d034..9292bce749 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -474,9 +474,6 @@ define(function (require, exports, module) { }, "Tab bar to become visible" ); - - // Verify the tab bar is visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(true); }); it("should hide tab bar when the feature is disabled", async function () { @@ -490,9 +487,6 @@ define(function (require, exports, module) { }, "Tab bar to become hidden" ); - - // Verify the tab bar is not visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); }); it("should show working set when the option is enabled", async function () { @@ -506,9 +500,6 @@ define(function (require, exports, module) { }, "Working set to become visible" ); - - // Verify the working set is visible - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); }); it("should hide working set when the option is disabled", async function () { @@ -522,9 +513,6 @@ define(function (require, exports, module) { }, "Working set to become hidden" ); - - // Verify the working set is not visible - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); }); }); @@ -595,9 +583,6 @@ define(function (require, exports, module) { "Working set to become hidden" ); - // Verify the working set is hidden - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(true); - // Click the configure working set button again $configButton.click(); @@ -621,9 +606,6 @@ define(function (require, exports, module) { }, "Working set to become visible" ); - - // Verify the working set is visible - expect($("#working-set-list-container").hasClass("working-set-hidden")).toBe(false); }); it("should toggle tab bar visibility when 'Show file tab bar' option is clicked", async function () { @@ -661,9 +643,6 @@ define(function (require, exports, module) { "Tab bar to become hidden" ); - // Verify the tab bar is hidden - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); - // Click the configure working set button again $configButton.click(); @@ -687,9 +666,6 @@ define(function (require, exports, module) { }, "Tab bar to become visible" ); - - // Verify the tab bar is visible - expect($("#phoenix-tab-bar").is(":visible")).toBe(true); }); }); @@ -887,10 +863,6 @@ define(function (require, exports, module) { expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); expect(getPaneTabCount("second-pane")).toBe(secondPaneFiles.length + 1); - // Verify the tab is now in the second pane - expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); - expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); - // Clean up - close all files and reset to single pane await testWindow.closeAllFiles(); MainViewManager.setLayoutScheme(1, 1); @@ -1026,10 +998,6 @@ define(function (require, exports, module) { expect(getPaneTabCount("first-pane")).toBe(firstPaneFiles.length - 1); expect(getPaneTabCount("second-pane")).toBe(1); - // Verify the tab is now in the second pane - expect(tabExistsInPane(firstPaneFiles[0], "first-pane")).toBe(false); - expect(tabExistsInPane(firstPaneFiles[0], "second-pane")).toBe(true); - // Clean up - close all files and reset to single pane await testWindow.closeAllFiles(); MainViewManager.setLayoutScheme(1, 1); @@ -2478,10 +2446,6 @@ define(function (require, exports, module) { "Both tab bars to disappear" ); - // Verify both tab bars are hidden - expect($("#phoenix-tab-bar").is(":visible")).toBe(false); - expect($("#phoenix-tab-bar-2").is(":visible")).toBe(false); - // Clean up - close all files and reset to single pane await testWindow.closeAllFiles(); MainViewManager.setLayoutScheme(1, 1);