diff --git a/qa/qa/page/project/web_ide/vscode.rb b/qa/qa/page/project/web_ide/vscode.rb
index 63dc75b523f7b4c52f157a9d2b6a08ab54e5a948..8443f296411f463ebd641ac7ac8713a40c1a70c2 100644
--- a/qa/qa/page/project/web_ide/vscode.rb
+++ b/qa/qa/page/project/web_ide/vscode.rb
@@ -16,8 +16,11 @@ def has_file_explorer?
           end
 
           def right_click_file_explorer
-            has_element?('div.monaco-list-rows')
-            find_element('div[aria-label="Files Explorer"]').right_click
+            page.find('.explorer-folders-view', visible: true).right_click
+          end
+
+          def has_file?(file_name)
+            has_element?("div[aria-label='#{file_name}']")
           end
 
           def open_file_from_explorer(file_name)
@@ -36,19 +39,15 @@ def has_right_click_menu_item?
             has_element?('div.menu-item-check')
           end
 
-          def click_new_folder_menu_item
-            click_element('span[aria-label="New Folder..."]')
-          end
-
-          def has_committed_and_pushed_successfully?
-            page.has_css?('.span[title="Success! Your changes have been committed."]')
+          def click_menu_item(item)
+            click_element("li[title='#{item}']")
           end
 
           def click_upload_menu_item
             click_element('span[aria-label="Upload..."]')
           end
 
-          def enter_new_folder_text_input(name)
+          def enter_text_for_input(name)
             find_element('input[type="text"]')
             send_keys(name, :enter)
           end
@@ -93,28 +92,38 @@ def click_new_branch
           end
 
           def click_continue_with_existing_branch
-            page.find('.monaco-button[title="Continue"]').click
+            click_element('.monaco-button[title="Continue"]')
           end
 
           def has_branch_input_field?
             has_element?('input[aria-label="input"]')
           end
 
+          def has_committed_successfully?
+            has_element?('.span[title="Success! Your changes have been committed."]')
+          end
+
           def has_message?(content)
             within_vscode_editor do
               has_text?(content)
             end
           end
 
+          def close_ide_tab
+            page.execute_script "window.close();" if page.current_url.include?('ide')
+          end
+
+          def ide_tab_closed?
+            within_vscode_editor do
+              has_file_explorer?
+            end
+          end
+
           def within_vscode_editor(&block)
             iframe = find('#ide iframe')
             page.within_frame(iframe, &block)
           end
 
-          def click_new_file_menu_item
-            page.find('[aria-label="New File..."]').click
-          end
-
           def switch_to_original_window
             page.driver.browser.switch_to.window(page.driver.browser.window_handles.first)
           end
@@ -122,21 +131,16 @@ def switch_to_original_window
           def create_new_file_from_template(filename, template)
             within_vscode_editor do
               Support::Waiter.wait_until(max_duration: 20, retry_on_exception: true) do
-                click_new_file_menu_item
-                enter_new_file_text_input(filename)
+                click_menu_item("New File...")
+                enter_text_for_input(filename)
                 page.within('div.editor-container') do
                   page.find('textarea.inputarea.monaco-mouse-cursor-text').send_keys(template)
                 end
-                page.has_content?(filename)
+                has_text?(filename)
               end
             end
           end
 
-          def enter_new_file_text_input(name)
-            page.find('.explorer-item-edited', visible: true)
-            send_keys(name, :enter)
-          end
-
           # Used for stablility, due to feature_caching of vscode_web_ide
           def wait_for_ide_to_load
             page.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
@@ -156,18 +160,12 @@ def wait_for_ide_to_load
             end
           end
 
-          def create_new_folder(name)
-            within_vscode_editor do
-              # Use for stability, WebIDE inside an iframe is finnicky, webdriver sometimes moves too fast
-              Support::Waiter.wait_until(max_duration: 20, retry_on_exception: true) do
-                right_click_file_explorer
-                has_right_click_menu_item?
-                click_new_folder_menu_item
-                # Verify New Folder button is triggered and textbox is waiting for input
-                enter_new_folder_text_input(name)
-                has_text?(name)
-              end
-            end
+          def create_new_folder(folder_name)
+            create_item("New Folder...", folder_name)
+          end
+
+          def create_new_file(file_name)
+            create_item("New File...", file_name)
           end
 
           def commit_and_push(file_name)
@@ -194,13 +192,13 @@ def commit_toggle(message)
           def push_to_existing_branch
             within_vscode_editor do
               click_continue_with_existing_branch
-              has_committed_and_pushed_successfully?
+              has_committed_successfully?
             end
           end
 
           def push_to_new_branch
             within_vscode_editor do
-              click_new_branch
+              page.find('.monaco-button[title="Create new branch"]').click
               has_branch_input_field?
               # Typing enter to 'New branch name' popup to take the default branch name
               send_keys(:enter)
@@ -260,6 +258,20 @@ def validate_prompt(pattern)
               end
             end
           end
+
+          private
+
+          def create_item(click_item, item_name)
+            within_vscode_editor do
+              # Use for stability, WebIDE inside an iframe is finnicky, webdriver sometimes moves too fast
+              Support::Waiter.wait_until(max_duration: 20, retry_on_exception: true) do
+                click_menu_item(click_item)
+                # Verify the button is triggered and textbox is waiting for input
+                enter_text_for_input(item_name)
+                has_text?(item_name)
+              end
+            end
+          end
         end
       end
     end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7df73268c836936e96ea2bdeb13db7aee0feab6e
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module QA
+  RSpec.describe 'Create', product_group: :ide do
+    describe 'Closing Web IDE' do
+      let(:file_name) { 'file.txt' }
+      let(:project) { create(:project, :with_readme, name: 'webide-close-with-unsaved-changes') }
+
+      before do
+        Flow::Login.sign_in
+        project.visit!
+        Page::Project::Show.perform(&:open_web_ide!)
+        Page::Project::WebIDE::VSCode.perform(&:wait_for_ide_to_load)
+      end
+
+      it 'shows an alert when there are unsaved changes',
+        testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/411298' do
+        Page::Project::WebIDE::VSCode.perform do |ide|
+          ide.create_new_file(file_name)
+          ide.has_file?(file_name)
+          ide.close_ide_tab
+          expect do
+            ide.ide_tab_closed?
+          end.to raise_error(Selenium::WebDriver::Error::UnexpectedAlertOpenError, /unexpected alert open/)
+        end
+      end
+    end
+  end
+end