diff --git a/src/extensions/default/Git/src/Main.js b/src/extensions/default/Git/src/Main.js index 3b50a63ed7..1d05933d6d 100644 --- a/src/extensions/default/Git/src/Main.js +++ b/src/extensions/default/Git/src/Main.js @@ -344,27 +344,35 @@ define(function (require, exports) { var _toggleMenuEntriesState = false, _divider1 = null, - _divider2 = null; + _divider2 = null, + _divider3 = null; function toggleMenuEntries(bool) { if (bool === _toggleMenuEntriesState) { return; } var projectCmenu = Menus.getContextMenu(Menus.ContextMenuIds.PROJECT_MENU); var workingCmenu = Menus.getContextMenu(Menus.ContextMenuIds.WORKING_SET_CONTEXT_MENU); + var tabbarCmenu = Menus.getContextMenu("tabbar-context-menu"); if (bool) { _divider1 = projectCmenu.addMenuDivider(); _divider2 = workingCmenu.addMenuDivider(); + _divider3 = tabbarCmenu.addMenuDivider(); projectCmenu.addMenuItem(CMD_ADD_TO_IGNORE); workingCmenu.addMenuItem(CMD_ADD_TO_IGNORE); + tabbarCmenu.addMenuItem(CMD_ADD_TO_IGNORE); projectCmenu.addMenuItem(CMD_REMOVE_FROM_IGNORE); workingCmenu.addMenuItem(CMD_REMOVE_FROM_IGNORE); + tabbarCmenu.addMenuItem(CMD_REMOVE_FROM_IGNORE); } else { projectCmenu.removeMenuDivider(_divider1.id); workingCmenu.removeMenuDivider(_divider2.id); + tabbarCmenu.removeMenuDivider(_divider3.id); projectCmenu.removeMenuItem(CMD_ADD_TO_IGNORE); workingCmenu.removeMenuItem(CMD_ADD_TO_IGNORE); + tabbarCmenu.removeMenuItem(CMD_ADD_TO_IGNORE); projectCmenu.removeMenuItem(CMD_REMOVE_FROM_IGNORE); workingCmenu.removeMenuItem(CMD_REMOVE_FROM_IGNORE); + tabbarCmenu.removeMenuItem(CMD_REMOVE_FROM_IGNORE); } _toggleMenuEntriesState = bool; } diff --git a/src/extensions/default/Git/src/ProjectTreeMarks.js b/src/extensions/default/Git/src/ProjectTreeMarks.js index 966d4d5686..faeb6e1e23 100644 --- a/src/extensions/default/Git/src/ProjectTreeMarks.js +++ b/src/extensions/default/Git/src/ProjectTreeMarks.js @@ -201,4 +201,8 @@ define(function (require) { detachEvents(); }); + return { + isIgnored: isIgnored + }; + }); diff --git a/src/extensions/default/Git/src/TabBarIntegration.js b/src/extensions/default/Git/src/TabBarIntegration.js index 24453f3166..0336cf6a87 100644 --- a/src/extensions/default/Git/src/TabBarIntegration.js +++ b/src/extensions/default/Git/src/TabBarIntegration.js @@ -3,6 +3,7 @@ define(function (require) { const Events = require("src/Events"); const Git = require("src/git/Git"); const Preferences = require("src/Preferences"); + const ProjectTreeMarks = require("src/ProjectTreeMarks"); // the cache of file statuses by path let fileStatusCache = {}; @@ -55,6 +56,19 @@ define(function (require) { ); } + /** + * whether the file is gitignored or not + * + * @param {string} fullPath - the file path + * @returns {boolean} - if the file is gitignored it returns true otherwise false + */ + function isIgnored(fullPath) { + if (!ProjectTreeMarks || !ProjectTreeMarks.isIgnored) { + return false; + } + return ProjectTreeMarks.isIgnored(fullPath); + } + // Update file status cache when Git status results are received EventEmitter.on(Events.GIT_STATUS_RESULTS, function (files) { @@ -85,6 +99,7 @@ define(function (require) { return { getFileStatus: getFileStatus, isModified: isModified, - isUntracked: isUntracked + isUntracked: isUntracked, + isIgnored: isIgnored }; }); diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index bf5d0c4c27..fd6ae9975e 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -150,9 +150,14 @@ define(function (require, exports, module) { const TabBarIntegration = window.phoenixGitEvents.TabBarIntegration; // find the Git status - // if untracked we add the git-new class and U char - // if modified we add the git-modified class and M char - if (TabBarIntegration.isUntracked(entry.path)) { + // Check ignored FIRST (takes precedence over other statuses) + // if ignored we add the git-ignored class + // if untracked we add the git-new class + // if modified we add the git-modified class + if (TabBarIntegration.isIgnored(entry.path)) { + gitStatus = "Ignored"; + gitStatusClass = "git-ignored"; + } else if (TabBarIntegration.isUntracked(entry.path)) { gitStatus = "Untracked"; gitStatusClass = "git-new"; } else if (TabBarIntegration.isModified(entry.path)) { @@ -553,7 +558,7 @@ define(function (require, exports, module) { const paneId = isSecondPane ? "second-pane" : "first-pane"; // show the context menu at mouse position - MoreOptions.showMoreOptionsContextMenu(paneId, event.pageX, event.pageY, filePath); + MoreOptions.showMoreOptionsContextMenu(paneId, filePath, event.pageX, event.pageY); }); } @@ -768,6 +773,7 @@ define(function (require, exports, module) { // handle when a single tab gets clicked handleTabClick(); + MoreOptions.init(); Overflow.init(); DragDrop.init($("#phoenix-tab-bar"), $("#phoenix-tab-bar-2")); diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index d8bbd41d91..b6df56139a 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -23,308 +23,143 @@ * The more options context menu is shown when a tab is right-clicked */ define(function (require, exports, module) { - const DropdownButton = require("widgets/DropdownButton"); - const Strings = require("strings"); const CommandManager = require("command/CommandManager"); const Commands = require("command/Commands"); const FileSystem = require("filesystem/FileSystem"); + const Menus = require("command/Menus"); + const Strings = require("strings"); const Global = require("./global"); - // List of items to show in the context menu - // Strings defined in `src/nls/root/strings.js` - const items = [ - Strings.CLOSE_TAB, - Strings.CLOSE_TABS_TO_THE_LEFT, - Strings.CLOSE_TABS_TO_THE_RIGHT, - Strings.CLOSE_SAVED_TABS, - Strings.CLOSE_ALL_TABS, - "---", - Strings.CMD_FILE_RENAME, - Strings.CMD_FILE_DELETE, - Strings.CMD_SHOW_IN_TREE, - "---", - Strings.REOPEN_CLOSED_FILE - ]; - - /** - * "CLOSE TAB" - * this function handles the closing of the tab that was right-clicked - * - * @param {String} filePath - path of the file to close - * @param {String} paneId - the id of the pane in which the file is present - */ - function handleCloseTab(filePath, paneId) { - if (filePath) { - // Get the file object using FileSystem - const fileObj = FileSystem.getFileForPath(filePath); - - // Execute close command with file object and pane ID - CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); - } + // these are Tab bar specific commands for the context menu + // not added in the Commands.js as Tab bar is not a core module but an extension + // read init function + const TABBAR_CLOSE_TABS_LEFT = "tabbar.closeTabsLeft"; + const TABBAR_CLOSE_TABS_RIGHT = "tabbar.closeTabsRight"; + const TABBAR_CLOSE_SAVED_TABS = "tabbar.closeSavedTabs"; + const TABBAR_CLOSE_ALL = "tabbar.closeAllTabs"; + + // stores the context of the right-clicked tab (which file, which pane) + // this is set inside the showMoreOptionsContextMenu. read that func for more details + let _currentTabContext = { filePath: null, paneId: null }; + + // gets the working set (list of open files) for the given pane + function _getWorkingSet(paneId) { + return paneId === "first-pane" ? Global.firstPaneWorkingSet : Global.secondPaneWorkingSet; } - /** - * "CLOSE ALL TABS" - * This will close all tabs in the specified pane - * - * @param {String} paneId - the id of the pane ["first-pane", "second-pane"] - */ - function handleCloseAllTabs(paneId) { - if (!paneId) { - return; - } - - let workingSet; - workingSet = paneId === "first-pane" ? Global.firstPaneWorkingSet : Global.secondPaneWorkingSet; - if (!workingSet || workingSet.length === 0) { - return; - } - - // close each file in the pane, start from the rightmost [to avoid index shifts] - for (let i = workingSet.length - 1; i >= 0; i--) { - const fileObj = FileSystem.getFileForPath(workingSet[i].path); + // closes files from right to left to avoid index shifts during iteration + function _closeFiles(files, paneId) { + for (let i = files.length - 1; i >= 0; i--) { + const fileObj = FileSystem.getFileForPath(files[i].path); CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); } } - /** - * "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 handleCloseSavedTabs(paneId) { - if (!paneId) { - return; - } - - let workingSet; - workingSet = paneId === "first-pane" ? Global.firstPaneWorkingSet : Global.secondPaneWorkingSet; - if (!workingSet || workingSet.length === 0) { - return; - } - - // get all those entries that are not dirty - const unmodifiedEntries = workingSet.filter((entry) => !entry.isDirty); - - // 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 }); - } - } - - /** - * "CLOSE TABS TO THE LEFT" - * This function is responsible for closing all tabs to the left of the right-clicked tab - * - * @param {String} filePath - path of the file that was right-clicked - * @param {String} paneId - the id of the pane in which the file is present - */ - function handleCloseTabsToTheLeft(filePath, paneId) { - if (!filePath) { - return; - } - - let workingSet; - workingSet = paneId === "first-pane" ? Global.firstPaneWorkingSet : Global.secondPaneWorkingSet; - if (!workingSet) { - return; - } - - // find the index of the current file in the working set - const currentIndex = workingSet.findIndex((entry) => entry.path === filePath); - - if (currentIndex > 0) { - // we only proceed if there are tabs to the left - // get all files to the left of the current file - const filesToClose = workingSet.slice(0, currentIndex); - - // Close each file, starting from the rightmost [to avoid index shifts] - for (let i = filesToClose.length - 1; i >= 0; i--) { - const fileObj = FileSystem.getFileForPath(filesToClose[i].path); - CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); - } + // executes a command with the right-clicked tab's file as the target + function _executeWithFileContext(commandId, options = {}) { + if (_currentTabContext.filePath) { + // we need to get the file object from the file path, as the commandManager expects the file object + const fileObj = FileSystem.getFileForPath(_currentTabContext.filePath); + CommandManager.execute(commandId, { file: fileObj, ...options }); } } - /** - * "CLOSE TABS TO THE RIGHT" - * This function is responsible for closing all tabs to the right of the right-clicked tab - * - * @param {String} filePath - path of the file that was right-clicked - * @param {String} paneId - the id of the pane in which the file is present - */ - function handleCloseTabsToTheRight(filePath, paneId) { - if (!filePath) { - return; - } - - let workingSet; - workingSet = paneId === "first-pane" ? Global.firstPaneWorkingSet : Global.secondPaneWorkingSet; - if (!workingSet) { - return; - } - - // get the index of the current file in the working set - const currentIndex = workingSet.findIndex((entry) => entry.path === filePath); - // only proceed if there are tabs to the right - if (currentIndex !== -1 && currentIndex < workingSet.length - 1) { - // get all files to the right of the current file - const filesToClose = workingSet.slice(currentIndex + 1); - - for (let i = filesToClose.length - 1; i >= 0; i--) { - const fileObj = FileSystem.getFileForPath(filesToClose[i].path); - CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); - } + // **Close All Tabs** + // closes all tabs in the pane where the tab was right-clicked + function handleCloseAllTabs() { + const workingSet = _getWorkingSet(_currentTabContext.paneId); + if (workingSet && workingSet.length !== 0) { + // close everything in the pane + _closeFiles(workingSet, _currentTabContext.paneId); } } - /** - * "REOPEN CLOSED FILE" - * This just calls the reopen closed file command. everthing else is handled there - * TODO: disable the command if there are no closed files, look into the file menu - */ - function reopenClosedFile() { - CommandManager.execute(Commands.FILE_REOPEN_CLOSED); - } - - /** - * "RENAME FILE" - * This function handles the renaming of the file that was right-clicked - * - * @param {String} filePath - path of the file to rename - */ - function handleFileRename(filePath) { - if (filePath) { - // First ensure the sidebar is visible so users can see the rename action - CommandManager.execute(Commands.SHOW_SIDEBAR); - - // Get the file object using FileSystem - const fileObj = FileSystem.getFileForPath(filePath); - - // Execute the rename command with the file object - CommandManager.execute(Commands.FILE_RENAME, { file: fileObj }); + // **Close Saved Tabs** + // closes all saved tabs (not dirty) in the pane + function handleCloseSavedTabs() { + const workingSet = _getWorkingSet(_currentTabContext.paneId); + if (workingSet && workingSet.length !== 0) { + // filter out dirty tabs, only close the saved ones + const savedTabs = workingSet.filter(entry => !entry.isDirty); + _closeFiles(savedTabs, _currentTabContext.paneId); } } - /** - * "DELETE FILE" - * This function handles the deletion of the file that was right-clicked - * - * @param {String} filePath - path of the file to delete - */ - function handleFileDelete(filePath) { - if (filePath) { - // Get the file object using FileSystem - const fileObj = FileSystem.getFileForPath(filePath); + // **Close Tabs to the Left** + // closes all tabs to the left of the right-clicked tab + function handleCloseTabsToTheLeft() { + const workingSet = _getWorkingSet(_currentTabContext.paneId); + if (!workingSet) { return; } - // Execute the delete command with the file object - CommandManager.execute(Commands.FILE_DELETE, { file: fileObj }); + // find where the right-clicked tab is in the list + const currentIndex = workingSet.findIndex(entry => entry.path === _currentTabContext.filePath); + if (currentIndex > 0) { + // slice from start to currentIndex (not including currentIndex) + _closeFiles(workingSet.slice(0, currentIndex), _currentTabContext.paneId); } } - /** - * "SHOW IN FILE TREE" - * This function handles showing the file in the file tree - * - * @param {String} filePath - path of the file to show in file tree - */ - function handleShowInFileTree(filePath) { - if (filePath) { - // First ensure the sidebar is visible so users can see the file in the tree - CommandManager.execute(Commands.SHOW_SIDEBAR); - - // Get the file object using FileSystem - const fileObj = FileSystem.getFileForPath(filePath); + // **Close Tabs to the Right** + // closes all tabs to the right of the right-clicked tab + function handleCloseTabsToTheRight() { + const workingSet = _getWorkingSet(_currentTabContext.paneId); + if (!workingSet) { return; } - // Execute the show in tree command with the file object - CommandManager.execute(Commands.NAVIGATE_SHOW_IN_FILE_TREE, { file: fileObj }); + // find where the right-clicked tab is in the list + const currentIndex = workingSet.findIndex(entry => entry.path === _currentTabContext.filePath); + if (currentIndex !== -1 && currentIndex < workingSet.length - 1) { + // slice from currentIndex+1 to end + _closeFiles(workingSet.slice(currentIndex + 1), _currentTabContext.paneId); } } /** - * This function is called when a tab is right-clicked - * This will show the more options context menu - * + * this function is called from Tabbar/main.js when a tab is right clicked + * it is responsible to show the context menu and also set the currentTabContext * @param {String} paneId - the id of the pane ["first-pane", "second-pane"] + * @param {String} filePath - the path of the file that was right-clicked * @param {Number} x - the x coordinate for positioning the menu * @param {Number} y - the y coordinate for positioning the menu - * @param {String} filePath - [optional] the path of the file that was right-clicked */ - function showMoreOptionsContextMenu(paneId, x, y, filePath) { - const dropdown = new DropdownButton.DropdownButton("", items); - - // Append to document body for absolute positioning - $("body").append(dropdown.$button); - - // Position the dropdown at the mouse coordinates - dropdown.$button.css({ - position: "absolute", - left: x + "px", - top: y + "px", - zIndex: 1000 - }); + function showMoreOptionsContextMenu(paneId, filePath, x, y) { + _currentTabContext.filePath = filePath; + _currentTabContext.paneId = paneId; - // Add a custom class to override the max-height, not sure why a scroll bar was coming but this did the trick - dropdown.dropdownExtraClasses = "tabbar-context-menu"; - - dropdown.showDropdown(); - - $(".tabbar-context-menu").css("max-height", "300px"); - - // handle the option selection - dropdown.on("select", function (e, item) { - _handleSelection(item, filePath, paneId); - }); - - // Remove the button after the dropdown is hidden - dropdown.$button.css({ - display: "none" - }); + const contextMenu = Menus.getContextMenu("tabbar-context-menu"); + const event = $.Event("contextmenu", { pageX: x, pageY: y }); + contextMenu.open(event); } + /** - * Handles the selection of an option in the more options context menu - * - * @param {String} item - the item being selected - * @param {String} filePath - the path of the file that was right-clicked - * @param {String} paneId - the id of the pane ["first-pane", "second-pane"] + * this is the main function, it gets called only once on load from TabBar/main.js + * this registers the context menu and add the menu items inside it */ - function _handleSelection(item, filePath, paneId) { - switch (item) { - case Strings.CLOSE_TAB: - handleCloseTab(filePath, paneId); - break; - case Strings.CLOSE_TABS_TO_THE_LEFT: - handleCloseTabsToTheLeft(filePath, paneId); - break; - case Strings.CLOSE_TABS_TO_THE_RIGHT: - handleCloseTabsToTheRight(filePath, paneId); - break; - case Strings.CLOSE_ALL_TABS: - handleCloseAllTabs(paneId); - break; - case Strings.CLOSE_SAVED_TABS: - handleCloseSavedTabs(paneId); - break; - case Strings.CMD_FILE_RENAME: - handleFileRename(filePath); - break; - case Strings.CMD_FILE_DELETE: - handleFileDelete(filePath); - break; - case Strings.CMD_SHOW_IN_TREE: - handleShowInFileTree(filePath); - break; - case Strings.REOPEN_CLOSED_FILE: - reopenClosedFile(); - break; - } + function init() { + // these are the tab bar specific commands + CommandManager.register(Strings.CLOSE_TABS_TO_THE_LEFT, TABBAR_CLOSE_TABS_LEFT, handleCloseTabsToTheLeft); + CommandManager.register(Strings.CLOSE_TABS_TO_THE_RIGHT, TABBAR_CLOSE_TABS_RIGHT, handleCloseTabsToTheRight); + CommandManager.register(Strings.CLOSE_SAVED_TABS, TABBAR_CLOSE_SAVED_TABS, handleCloseSavedTabs); + CommandManager.register(Strings.CLOSE_ALL_TABS, TABBAR_CLOSE_ALL, handleCloseAllTabs); + + // these commands already exist for working files, just reusing them + const menu = Menus.registerContextMenu("tabbar-context-menu"); + menu.addMenuItem(Commands.FILE_CLOSE); + menu.addMenuItem(TABBAR_CLOSE_TABS_LEFT); + menu.addMenuItem(TABBAR_CLOSE_TABS_RIGHT); + menu.addMenuItem(TABBAR_CLOSE_SAVED_TABS); + menu.addMenuItem(TABBAR_CLOSE_ALL); + menu.addMenuDivider(); + menu.addMenuItem(Commands.FILE_RENAME); + menu.addMenuItem(Commands.FILE_DELETE); + menu.addMenuItem(Commands.NAVIGATE_SHOW_IN_FILE_TREE); + menu.addMenuDivider(); + menu.addMenuItem(Commands.FILE_REOPEN_CLOSED); } module.exports = { - showMoreOptionsContextMenu + showMoreOptionsContextMenu, + init }; }); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 986e42901d..ed03f8e14b 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -469,12 +469,10 @@ define({ "STATUSBAR_TASKS_RESTART": "Restart", // Tab bar Strings - "CLOSE_TAB": "Close Tab", "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_SAVED_TABS": "Close Saved Tabs", - "REOPEN_CLOSED_FILE": "Reopen Closed File", // CodeInspection: errors/warnings "ERRORS_NO_FILE": "No File Open", @@ -1664,7 +1662,7 @@ define({ "CUSTOM_SNIPPETS_SAVE": "Save", "CUSTOM_SNIPPETS_NO_DESCRIPTION": "No description", "CUSTOM_SNIPPETS_NO_MATCHES": "No snippets match \"{0}\"", - "CUSTOM_SNIPPETS_LEARN_MORE": "Add your own code hints to speed up coding - Learn More", + "CUSTOM_SNIPPETS_LEARN_MORE": "Add your own code hints to speed up coding - Learn More", "CUSTOM_SNIPPETS_DUPLICATE_ERROR": "A snippet with abbreviation \"{0}\" already exists.", "CUSTOM_SNIPPETS_SPACE_ERROR": "Space is not accepted as a valid abbreviation character.", "CUSTOM_SNIPPETS_ABBR_LENGTH_ERROR": "Abbreviation cannot be more than 30 characters.", diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index 912425a9f7..59655a39fd 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -256,19 +256,19 @@ .tab.placeholder .tab-name { font-style: italic; - color: #888; + color: #999; } .dark .tab.placeholder .tab-name { - color: #888; + color: #999; } .tab.placeholder.active .tab-name { - color: #666; + color: #777; } .dark .tab.placeholder.active .tab-name { - color: #aaa; + color: #bbb; } .tab.placeholder::after { @@ -460,3 +460,21 @@ .dark .tab.git-new > .tab-icon:before { color: #91CC41; } + +.tab.git-ignored .tab-name { + color: #868888 !important; + font-style: italic; +} + +.tab.git-ignored .tab-dirname { + color: #868888 !important; + font-style: italic; +} + +.dark .tab.git-ignored .tab-name { + color: #868888 !important; +} + +.dark .tab.git-ignored .tab-dirname { + color: #868888 !important; +} diff --git a/test/spec/Extn-Tabbar-integ-test.js b/test/spec/Extn-Tabbar-integ-test.js index 9292bce749..1293801e96 100644 --- a/test/spec/Extn-Tabbar-integ-test.js +++ b/test/spec/Extn-Tabbar-integ-test.js @@ -1837,7 +1837,7 @@ define(function (require, exports, module) { * @returns {jQuery} - The context menu element */ function getContextMenu() { - return $(".tabbar-context-menu"); + return $("#tabbar-context-menu"); } it("should open context menu when right-clicking on a tab", async function () { @@ -1846,22 +1846,22 @@ define(function (require, exports, module) { expect($tab.length).toBe(1); // Simulate a right-click (contextmenu) event on the tab - $tab.trigger("contextmenu", { + const event = $.Event("contextmenu", { pageX: 100, pageY: 100 }); + $tab.trigger(event); // Wait for the context menu to appear await awaitsFor( function () { - return getContextMenu().length > 0; + return getContextMenu().hasClass("open"); }, "Context menu to appear" ); - // Verify the context menu is visible - expect(getContextMenu().length).toBe(1); - expect(getContextMenu().is(":visible")).toBe(true); + // Verify the context menu is open + expect(getContextMenu().hasClass("open")).toBe(true); // Clean up - close the context menu by clicking elsewhere $("body").click(); @@ -1869,36 +1869,47 @@ define(function (require, exports, module) { // Wait for the context menu to disappear await awaitsFor( function () { - return getContextMenu().length === 0; + return !getContextMenu().hasClass("open"); }, "Context menu to disappear" ); }); - it("should close the tab when selecting 'Close Tab' from context menu", async function () { + it("should close the tab when selecting 'Close' 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", { + // First trigger mousedown to make the tab active + const mousedownEvent = $.Event("mousedown", { + button: 2, pageX: 100, pageY: 100 }); + $tab.trigger(mousedownEvent); + + // Then trigger contextmenu to open the menu + const contextmenuEvent = $.Event("contextmenu", { + pageX: 100, + pageY: 100 + }); + $tab.trigger(contextmenuEvent); // Wait for context menu to appear await awaitsFor( function () { - return getContextMenu().length > 0; + return getContextMenu().hasClass("open"); }, "Context menu to appear" ); - // Find and click the "Close Tab" option + // Find and click the "Close" option const $closeTabOption = getContextMenu() - .find("a.stylesheet-link") + .find(".menu-name") .filter(function () { - return $(this).text().trim() === Strings.CLOSE_TAB; - }); + return $(this).text().trim() === Strings.CMD_FILE_CLOSE; + }) + .closest("li"); expect($closeTabOption.length).toBe(1); $closeTabOption.click(); @@ -1946,25 +1957,34 @@ define(function (require, exports, module) { const $tab = getTab(testFilePath); // Right-click on the first tab to open context menu - $tab.trigger("contextmenu", { + const mousedownEvent = $.Event("mousedown", { + button: 2, pageX: 100, pageY: 100 }); + $tab.trigger(mousedownEvent); + + const contextmenuEvent = $.Event("contextmenu", { + pageX: 100, + pageY: 100 + }); + $tab.trigger(contextmenuEvent); // Wait for context menu to appear await awaitsFor( function () { - return getContextMenu().length > 0; + return getContextMenu().hasClass("open"); }, "Context menu to appear" ); - // Find and click the "Close tabs to the right" option + // Find and click the "Close Tabs to the Right" option const $closeTabsToRightOption = getContextMenu() - .find("a.stylesheet-link") + .find(".menu-name") .filter(function () { return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_RIGHT; - }); + }) + .closest("li"); expect($closeTabsToRightOption.length).toBe(1); $closeTabsToRightOption.click(); @@ -2018,25 +2038,34 @@ define(function (require, exports, module) { const $tab = getTab(testFilePath3); // Right-click on the third tab to open context menu - $tab.trigger("contextmenu", { + const mousedownEvent = $.Event("mousedown", { + button: 2, pageX: 100, pageY: 100 }); + $tab.trigger(mousedownEvent); + + const contextmenuEvent = $.Event("contextmenu", { + pageX: 100, + pageY: 100 + }); + $tab.trigger(contextmenuEvent); // Wait for context menu to appear await awaitsFor( function () { - return getContextMenu().length > 0; + return getContextMenu().hasClass("open"); }, "Context menu to appear" ); - // Find and click the "Close tabs to the left" option + // Find and click the "Close Tabs to the Left" option const $closeTabsToLeftOption = getContextMenu() - .find("a.stylesheet-link") + .find(".menu-name") .filter(function () { return $(this).text().trim() === Strings.CLOSE_TABS_TO_THE_LEFT; - }); + }) + .closest("li"); expect($closeTabsToLeftOption.length).toBe(1); $closeTabsToLeftOption.click(); @@ -2105,25 +2134,34 @@ define(function (require, exports, module) { const $tab = getTab(testFilePath); // Right-click on the tab to open context menu - $tab.trigger("contextmenu", { + const mousedownEvent = $.Event("mousedown", { + button: 2, + pageX: 100, + pageY: 100 + }); + $tab.trigger(mousedownEvent); + + const contextmenuEvent = $.Event("contextmenu", { pageX: 100, pageY: 100 }); + $tab.trigger(contextmenuEvent); // Wait for context menu to appear await awaitsFor( function () { - return getContextMenu().length > 0; + return getContextMenu().hasClass("open"); }, "Context menu to appear" ); - // Find and click the "Close saved tabs" option + // Find and click the "Close Saved Tabs" option const $closeSavedTabsOption = getContextMenu() - .find("a.stylesheet-link") + .find(".menu-name") .filter(function () { return $(this).text().trim() === Strings.CLOSE_SAVED_TABS; - }); + }) + .closest("li"); expect($closeSavedTabsOption.length).toBe(1); $closeSavedTabsOption.click(); @@ -2184,25 +2222,34 @@ define(function (require, exports, module) { const $tab = getTab(testFilePath); // Right-click on the tab to open context menu - $tab.trigger("contextmenu", { + const mousedownEvent = $.Event("mousedown", { + button: 2, + pageX: 100, + pageY: 100 + }); + $tab.trigger(mousedownEvent); + + const contextmenuEvent = $.Event("contextmenu", { pageX: 100, pageY: 100 }); + $tab.trigger(contextmenuEvent); // Wait for context menu to appear await awaitsFor( function () { - return getContextMenu().length > 0; + return getContextMenu().hasClass("open"); }, "Context menu to appear" ); - // Find and click the "Close all tabs" option + // Find and click the "Close All Tabs" option const $closeAllTabsOption = getContextMenu() - .find("a.stylesheet-link") + .find(".menu-name") .filter(function () { return $(this).text().trim() === Strings.CLOSE_ALL_TABS; - }); + }) + .closest("li"); expect($closeAllTabsOption.length).toBe(1); $closeAllTabsOption.click();