This is an automated email from the ASF dual-hosted git repository.

chanholee pushed a commit to branch branch-0.12
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/branch-0.12 by this push:
     new b776fd67b2 [ZEPPELIN-6359] Add E2E tests about Home/Dashboard Page for 
New UI
b776fd67b2 is described below

commit b776fd67b206d0ae454e70cfc225c97b12a6361f
Author: YONGJAE LEE(이용재) <[email protected]>
AuthorDate: Sun Oct 26 17:16:13 2025 +0900

    [ZEPPELIN-6359] Add E2E tests about Home/Dashboard Page for New UI
    
    ### What is this PR for?
    Addition and improvement of Home/Dashboard-related E2E tests for New UI
    
    ---
    
    PAGES.WORKSPACE.HOME
    
    → src/app/pages/workspace/home/home.component
    
    PAGES.WORKSPACE.MAIN
    
    → src/app/pages/workspace/workspace.component
    
    ### What type of PR is it?
    Improvement
    
    ### Todos
    
    ### What is the Jira issue?
    ZEPPELIN-6359
    
    ### How should this be tested?
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Does the license files need to update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Closes #5102 from dididy/e2e/home.
    
    Signed-off-by: ChanHo Lee <[email protected]>
    (cherry picked from commit 8432cf66e73b39d003b169cd86d8604e871349e6)
    Signed-off-by: ChanHo Lee <[email protected]>
---
 zeppelin-web-angular/e2e/models/home-page.ts       | 155 ++++++++++--
 zeppelin-web-angular/e2e/models/home-page.util.ts  | 184 +++++++++++----
 zeppelin-web-angular/e2e/models/workspace-page.ts  |  32 +++
 .../e2e/models/workspace-page.util.ts              |  74 ++++++
 .../anonymous-login-redirect.spec.ts               |   8 +-
 .../e2e/tests/home/home-page-elements.spec.ts      | 172 ++++++++++++++
 .../home/home-page-enhanced-functionality.spec.ts  |  64 +++++
 .../tests/home/home-page-external-links.spec.ts    | 169 ++++++++++++++
 .../e2e/tests/home/home-page-layout.spec.ts        | 139 +++++++++++
 .../tests/home/home-page-note-operations.spec.ts   | 260 +++++++++++++++++++++
 .../tests/home/home-page-notebook-actions.spec.ts  |  68 ++++++
 .../e2e/tests/workspace/workspace-main.spec.ts     |  69 ++++++
 12 files changed, 1333 insertions(+), 61 deletions(-)

diff --git a/zeppelin-web-angular/e2e/models/home-page.ts 
b/zeppelin-web-angular/e2e/models/home-page.ts
index 7d24fdf3ed..872784dfa0 100644
--- a/zeppelin-web-angular/e2e/models/home-page.ts
+++ b/zeppelin-web-angular/e2e/models/home-page.ts
@@ -10,9 +10,9 @@
  * limitations under the License.
  */
 
-import { Locator, Page, expect } from '@playwright/test';
-import { BasePage } from './base-page';
+import { expect, Locator, Page } from '@playwright/test';
 import { getCurrentPath, waitForUrlNotContaining } from '../utils';
+import { BasePage } from './base-page';
 
 export class HomePage extends BasePage {
   readonly welcomeHeading: Locator;
@@ -25,19 +25,43 @@ export class HomePage extends BasePage {
   readonly filterInput: Locator;
   readonly zeppelinLogo: Locator;
   readonly anonymousUserIndicator: Locator;
-  readonly tutorialNotebooks: {
-    flinkTutorial: Locator;
-    pythonTutorial: Locator;
-    sparkTutorial: Locator;
-    rTutorial: Locator;
-    miscellaneousTutorial: Locator;
-  };
+  readonly welcomeSection: Locator;
+  readonly moreInfoGrid: Locator;
+  readonly notebookColumn: Locator;
+  readonly helpCommunityColumn: Locator;
+  readonly welcomeDescription: Locator;
+  readonly refreshNoteButton: Locator;
+  readonly refreshIcon: Locator;
+  readonly notebookList: Locator;
+  readonly notebookHeading: Locator;
+  readonly helpHeading: Locator;
+  readonly communityHeading: Locator;
   readonly externalLinks: {
     documentation: Locator;
     mailingList: Locator;
     issuesTracking: Locator;
     github: Locator;
   };
+  readonly nodeList: {
+    createNewNoteLink: Locator;
+    importNoteLink: Locator;
+    filterInput: Locator;
+    tree: Locator;
+    noteActions: {
+      renameNote: Locator;
+      clearOutput: Locator;
+      moveToTrash: Locator;
+    };
+    folderActions: {
+      createNote: Locator;
+      renameFolder: Locator;
+      moveToTrash: Locator;
+    };
+    trashActions: {
+      restoreAll: Locator;
+      emptyAll: Locator;
+    };
+  };
 
   constructor(page: Page) {
     super(page);
@@ -51,14 +75,17 @@ export class HomePage extends BasePage {
     this.filterInput = page.locator('input[placeholder*="Filter"]');
     this.zeppelinLogo = page.locator('text=Zeppelin').first();
     this.anonymousUserIndicator = page.locator('text=anonymous');
-
-    this.tutorialNotebooks = {
-      flinkTutorial: page.locator('text=Flink Tutorial'),
-      pythonTutorial: page.locator('text=Python Tutorial'),
-      sparkTutorial: page.locator('text=Spark Tutorial'),
-      rTutorial: page.locator('text=R Tutorial'),
-      miscellaneousTutorial: page.locator('text=Miscellaneous Tutorial')
-    };
+    this.welcomeSection = page.locator('.welcome');
+    this.moreInfoGrid = page.locator('.more-info');
+    this.notebookColumn = page.locator('[nz-col]').first();
+    this.helpCommunityColumn = page.locator('[nz-col]').last();
+    this.welcomeDescription = page.locator('.welcome').getByText('Zeppelin is 
web-based notebook');
+    this.refreshNoteButton = page.locator('a.refresh-note');
+    this.refreshIcon = page.locator('a.refresh-note i[nz-icon]');
+    this.notebookList = page.locator('zeppelin-node-list');
+    this.notebookHeading = this.notebookColumn.locator('h3');
+    this.helpHeading = page.locator('h3').filter({ hasText: 'Help' });
+    this.communityHeading = page.locator('h3').filter({ hasText: 'Community' 
});
 
     this.externalLinks = {
       documentation: page.locator('a[href*="zeppelin.apache.org/docs"]'),
@@ -66,6 +93,27 @@ export class HomePage extends BasePage {
       issuesTracking: page.locator('a[href*="issues.apache.org"]'),
       github: page.locator('a[href*="github.com/apache/zeppelin"]')
     };
+
+    this.nodeList = {
+      createNewNoteLink: page.locator('zeppelin-node-list a').filter({ 
hasText: 'Create new Note' }),
+      importNoteLink: page.locator('zeppelin-node-list a').filter({ hasText: 
'Import Note' }),
+      filterInput: page.locator('zeppelin-node-list 
input[placeholder*="Filter"]'),
+      tree: page.locator('zeppelin-node-list nz-tree'),
+      noteActions: {
+        renameNote: page.locator('.file .operation a[nztooltiptitle*="Rename 
note"]'),
+        clearOutput: page.locator('.file .operation a[nztooltiptitle*="Clear 
output"]'),
+        moveToTrash: page.locator('.file .operation a[nztooltiptitle*="Move 
note to Trash"]')
+      },
+      folderActions: {
+        createNote: page.locator('.folder .operation a[nztooltiptitle*="Create 
new note"]'),
+        renameFolder: page.locator('.folder .operation 
a[nztooltiptitle*="Rename folder"]'),
+        moveToTrash: page.locator('.folder .operation a[nztooltiptitle*="Move 
folder to Trash"]')
+      },
+      trashActions: {
+        restoreAll: page.locator('.folder .operation 
a[nztooltiptitle*="Restore all"]'),
+        emptyAll: page.locator('.folder .operation a[nztooltiptitle*="Empty 
all"]')
+      }
+    };
   }
 
   async navigateToHome(): Promise<void> {
@@ -113,4 +161,77 @@ export class HomePage extends BasePage {
   async getPageTitle(): Promise<string> {
     return this.page.title();
   }
+
+  async getWelcomeHeadingText(): Promise<string> {
+    const text = await this.welcomeHeading.textContent();
+    return text || '';
+  }
+
+  async getWelcomeDescriptionText(): Promise<string> {
+    const text = await this.welcomeDescription.textContent();
+    return text || '';
+  }
+
+  async clickRefreshNotes(): Promise<void> {
+    await this.refreshNoteButton.click();
+  }
+
+  async isNotebookListVisible(): Promise<boolean> {
+    return this.notebookList.isVisible();
+  }
+
+  async clickCreateNewNote(): Promise<void> {
+    await this.nodeList.createNewNoteLink.click();
+  }
+
+  async clickImportNote(): Promise<void> {
+    await this.nodeList.importNoteLink.click();
+  }
+
+  async filterNotes(searchTerm: string): Promise<void> {
+    await this.nodeList.filterInput.fill(searchTerm);
+  }
+
+  async isRefreshIconSpinning(): Promise<boolean> {
+    const spinAttribute = await this.refreshIcon.getAttribute('nzSpin');
+    return spinAttribute === 'true' || spinAttribute === '';
+  }
+
+  async waitForRefreshToComplete(): Promise<void> {
+    await this.page.waitForFunction(
+      () => {
+        const icon = document.querySelector('a.refresh-note i[nz-icon]');
+        return icon && !icon.hasAttribute('nzSpin');
+      },
+      { timeout: 10000 }
+    );
+  }
+
+  async getDocumentationLinkHref(): Promise<string | null> {
+    return this.externalLinks.documentation.getAttribute('href');
+  }
+
+  async areExternalLinksVisible(): Promise<boolean> {
+    const links = [
+      this.externalLinks.documentation,
+      this.externalLinks.mailingList,
+      this.externalLinks.issuesTracking,
+      this.externalLinks.github
+    ];
+
+    for (const link of links) {
+      if (!(await link.isVisible())) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  async isWelcomeSectionVisible(): Promise<boolean> {
+    return this.welcomeSection.isVisible();
+  }
+
+  async isMoreInfoGridVisible(): Promise<boolean> {
+    return this.moreInfoGrid.isVisible();
+  }
 }
diff --git a/zeppelin-web-angular/e2e/models/home-page.util.ts 
b/zeppelin-web-angular/e2e/models/home-page.util.ts
index 4211a722c0..5a5a6ff210 100644
--- a/zeppelin-web-angular/e2e/models/home-page.util.ts
+++ b/zeppelin-web-angular/e2e/models/home-page.util.ts
@@ -10,9 +10,9 @@
  * limitations under the License.
  */
 
-import { Page, expect } from '@playwright/test';
+import { expect, Page } from '@playwright/test';
+import { getBasicPageMetadata } from '../utils';
 import { HomePage } from './home-page';
-import { getBasicPageMetadata, waitForUrlNotContaining } from '../utils';
 
 export class HomePageUtil {
   private homePage: HomePage;
@@ -44,13 +44,6 @@ export class HomePageUtil {
     };
   }
 
-  async verifyHomePageIntegrity(): Promise<void> {
-    await this.verifyHomePageElements();
-    await this.verifyNotebookFunctionalities();
-    await this.verifyTutorialNotebooks();
-    await this.verifyExternalLinks();
-  }
-
   async verifyHomePageElements(): Promise<void> {
     await expect(this.homePage.welcomeHeading).toBeVisible();
     await expect(this.homePage.notebookSection).toBeVisible();
@@ -58,34 +51,11 @@ export class HomePageUtil {
     await expect(this.homePage.communitySection).toBeVisible();
   }
 
-  async verifyNotebookFunctionalities(): Promise<void> {
-    await expect(this.homePage.createNewNoteButton).toBeVisible();
-    await expect(this.homePage.importNoteButton).toBeVisible();
-
-    const filterInputCount = await this.homePage.filterInput.count();
-    if (filterInputCount > 0) {
-      await expect(this.homePage.filterInput).toBeVisible();
-    }
-  }
-
-  async verifyTutorialNotebooks(): Promise<void> {
-    await expect(this.homePage.tutorialNotebooks.flinkTutorial).toBeVisible();
-    await expect(this.homePage.tutorialNotebooks.pythonTutorial).toBeVisible();
-    await expect(this.homePage.tutorialNotebooks.sparkTutorial).toBeVisible();
-    await expect(this.homePage.tutorialNotebooks.rTutorial).toBeVisible();
-    await 
expect(this.homePage.tutorialNotebooks.miscellaneousTutorial).toBeVisible();
-  }
-
   async verifyExternalLinks(): Promise<void> {
-    const docCount = await this.homePage.externalLinks.documentation.count();
-    const mailCount = await this.homePage.externalLinks.mailingList.count();
-    const issuesCount = await 
this.homePage.externalLinks.issuesTracking.count();
-    const githubCount = await this.homePage.externalLinks.github.count();
-
-    if (docCount > 0) await 
expect(this.homePage.externalLinks.documentation).toBeVisible();
-    if (mailCount > 0) await 
expect(this.homePage.externalLinks.mailingList).toBeVisible();
-    if (issuesCount > 0) await 
expect(this.homePage.externalLinks.issuesTracking).toBeVisible();
-    if (githubCount > 0) await 
expect(this.homePage.externalLinks.github).toBeVisible();
+    await expect(this.homePage.externalLinks.documentation).toBeVisible();
+    await expect(this.homePage.externalLinks.mailingList).toBeVisible();
+    await expect(this.homePage.externalLinks.issuesTracking).toBeVisible();
+    await expect(this.homePage.externalLinks.github).toBeVisible();
   }
 
   async testNavigationConsistency(): Promise<{
@@ -108,7 +78,7 @@ export class HomePageUtil {
     };
   }
 
-  async getPageMetadata(): Promise<{
+  async getHomePageMetadata(): Promise<{
     title: string;
     path: string;
     isAnonymous: boolean;
@@ -122,8 +92,142 @@ export class HomePageUtil {
     };
   }
 
-  async navigateToLoginAndWaitForRedirect(): Promise<void> {
-    await this.page.goto('/#/login', { waitUntil: 'load' });
-    await waitForUrlNotContaining(this.page, '#/login');
+  async verifyWelcomeSection(): Promise<void> {
+    await expect(this.homePage.welcomeSection).toBeVisible();
+    await expect(this.homePage.welcomeHeading).toBeVisible();
+
+    const headingText = await this.homePage.getWelcomeHeadingText();
+    expect(headingText.trim()).toBe('Welcome to Zeppelin!');
+
+    const welcomeText = await this.homePage.welcomeDescription.textContent();
+    expect(welcomeText).toContain('web-based notebook');
+    expect(welcomeText).toContain('interactive data analytics');
+  }
+
+  async verifyNotebookSection(): Promise<void> {
+    await expect(this.homePage.notebookSection).toBeVisible();
+    await expect(this.homePage.notebookHeading).toBeVisible();
+    await expect(this.homePage.refreshNoteButton).toBeVisible();
+
+    // Wait for notebook list to load with timeout
+    await this.page.waitForSelector('zeppelin-node-list', { timeout: 10000 });
+    await expect(this.homePage.notebookList).toBeVisible();
+
+    // Additional wait for content to load
+    await this.page.waitForTimeout(1000);
+  }
+
+  async verifyNotebookRefreshFunctionality(): Promise<void> {
+    await this.homePage.clickRefreshNotes();
+
+    // Wait for refresh operation to complete
+    await this.page.waitForTimeout(2000);
+
+    // Ensure the notebook list is still visible after refresh
+    await expect(this.homePage.notebookList).toBeVisible();
+    const isStillVisible = await this.homePage.isNotebookListVisible();
+    expect(isStillVisible).toBe(true);
+  }
+
+  async verifyHelpSection(): Promise<void> {
+    await expect(this.homePage.helpSection).toBeVisible();
+    await expect(this.homePage.helpHeading).toBeVisible();
+  }
+
+  async verifyCommunitySection(): Promise<void> {
+    await expect(this.homePage.communitySection).toBeVisible();
+    await expect(this.homePage.communityHeading).toBeVisible();
+  }
+
+  async testExternalLinkTargets(): Promise<{
+    documentationHref: string | null;
+    mailingListHref: string | null;
+    issuesTrackingHref: string | null;
+    githubHref: string | null;
+  }> {
+    // Get the parent links that contain the text
+    const docLink = this.page.locator('a').filter({ hasText: 'Zeppelin 
documentation' });
+    const mailLink = this.page.locator('a').filter({ hasText: 'Mailing list' 
});
+    const issuesLink = this.page.locator('a').filter({ hasText: 'Issues 
tracking' });
+    const githubLink = this.page.locator('a').filter({ hasText: 'Github' });
+
+    return {
+      documentationHref: await docLink.getAttribute('href'),
+      mailingListHref: await mailLink.getAttribute('href'),
+      issuesTrackingHref: await issuesLink.getAttribute('href'),
+      githubHref: await githubLink.getAttribute('href')
+    };
+  }
+
+  async verifyNotebookActions(): Promise<void> {
+    await expect(this.homePage.nodeList.createNewNoteLink).toBeVisible();
+    await expect(this.homePage.nodeList.importNoteLink).toBeVisible();
+    await expect(this.homePage.nodeList.filterInput).toBeVisible();
+    await expect(this.homePage.nodeList.tree).toBeVisible();
+  }
+
+  async testNotebookRefreshLoadingState(): Promise<void> {
+    const refreshButton = this.page.locator('a.refresh-note');
+    const refreshIcon = this.page.locator('a.refresh-note i[nz-icon]');
+
+    await expect(refreshButton).toBeVisible();
+    await expect(refreshIcon).toBeVisible();
+
+    await this.homePage.clickRefreshNotes();
+
+    await this.page.waitForTimeout(500);
+
+    await expect(refreshIcon).toBeVisible();
+  }
+
+  async verifyCreateNewNoteWorkflow(): Promise<void> {
+    await this.homePage.clickCreateNewNote();
+
+    await this.page.waitForFunction(
+      () => {
+        return document.querySelector('zeppelin-note-create') !== null;
+      },
+      { timeout: 10000 }
+    );
+  }
+
+  async verifyImportNoteWorkflow(): Promise<void> {
+    await this.homePage.clickImportNote();
+
+    await this.page.waitForFunction(
+      () => {
+        return document.querySelector('zeppelin-note-import') !== null;
+      },
+      { timeout: 10000 }
+    );
+  }
+
+  async testFilterFunctionality(filterTerm: string): Promise<void> {
+    await this.homePage.filterNotes(filterTerm);
+
+    await this.page.waitForTimeout(1000);
+
+    const filteredResults = await this.page.locator('nz-tree .node').count();
+    expect(filteredResults).toBeGreaterThanOrEqual(0);
+  }
+
+  async verifyDocumentationVersionLink(): Promise<void> {
+    const href = await this.homePage.getDocumentationLinkHref();
+    expect(href).toContain('zeppelin.apache.org/docs');
+    expect(href).toMatch(/\/docs\/\d+\.\d+\.\d+(-SNAPSHOT)?\//);
+  }
+
+  async verifyAllExternalLinksTargetBlank(): Promise<void> {
+    const links = [
+      this.homePage.externalLinks.documentation,
+      this.homePage.externalLinks.mailingList,
+      this.homePage.externalLinks.issuesTracking,
+      this.homePage.externalLinks.github
+    ];
+
+    for (const link of links) {
+      const target = await link.getAttribute('target');
+      expect(target).toBe('_blank');
+    }
   }
 }
diff --git a/zeppelin-web-angular/e2e/models/workspace-page.ts 
b/zeppelin-web-angular/e2e/models/workspace-page.ts
new file mode 100644
index 0000000000..57c0da8796
--- /dev/null
+++ b/zeppelin-web-angular/e2e/models/workspace-page.ts
@@ -0,0 +1,32 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Locator, Page } from '@playwright/test';
+import { BasePage } from './base-page';
+
+export class WorkspacePage extends BasePage {
+  readonly workspaceComponent: Locator;
+  readonly header: Locator;
+  readonly routerOutlet: Locator;
+
+  constructor(page: Page) {
+    super(page);
+    this.workspaceComponent = page.locator('zeppelin-workspace');
+    this.header = page.locator('zeppelin-header');
+    this.routerOutlet = page.locator('zeppelin-workspace router-outlet');
+  }
+
+  async navigateToWorkspace(): Promise<void> {
+    await this.page.goto('/', { waitUntil: 'load' });
+    await this.waitForPageLoad();
+  }
+}
diff --git a/zeppelin-web-angular/e2e/models/workspace-page.util.ts 
b/zeppelin-web-angular/e2e/models/workspace-page.util.ts
new file mode 100644
index 0000000000..7ff706f93a
--- /dev/null
+++ b/zeppelin-web-angular/e2e/models/workspace-page.util.ts
@@ -0,0 +1,74 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, Page } from '@playwright/test';
+import { performLoginIfRequired, waitForZeppelinReady } from '../utils';
+import { WorkspacePage } from './workspace-page';
+
+export class WorkspaceTestUtil {
+  private page: Page;
+  private workspacePage: WorkspacePage;
+
+  constructor(page: Page) {
+    this.page = page;
+    this.workspacePage = new WorkspacePage(page);
+  }
+
+  async navigateAndWaitForLoad(): Promise<void> {
+    await this.workspacePage.navigateToWorkspace();
+    await waitForZeppelinReady(this.page);
+    await performLoginIfRequired(this.page);
+  }
+
+  async verifyWorkspaceLayout(): Promise<void> {
+    await expect(this.workspacePage.workspaceComponent).toBeVisible();
+    await expect(this.workspacePage.routerOutlet).toBeAttached();
+  }
+
+  async verifyHeaderVisibility(shouldBeVisible: boolean): Promise<void> {
+    if (shouldBeVisible) {
+      await expect(this.workspacePage.header).toBeVisible();
+    } else {
+      await expect(this.workspacePage.header).toBeHidden();
+    }
+  }
+
+  async verifyWorkspaceContainer(): Promise<void> {
+    await expect(this.workspacePage.workspaceComponent).toBeVisible();
+    const contentElements = await this.page.locator('.content').count();
+    expect(contentElements).toBeGreaterThan(0);
+  }
+
+  async verifyRouterOutletActivation(): Promise<void> {
+    await expect(this.workspacePage.routerOutlet).toBeAttached();
+
+    await this.page.waitForFunction(
+      () => {
+        const workspace = document.querySelector('zeppelin-workspace');
+        const outlet = workspace?.querySelector('router-outlet');
+        return outlet && outlet.nextElementSibling !== null;
+      },
+      { timeout: 10000 }
+    );
+  }
+
+  async waitForComponentActivation(): Promise<void> {
+    await this.page.waitForFunction(
+      () => {
+        const workspace = document.querySelector('zeppelin-workspace');
+        const content = workspace?.querySelector('.content');
+        return content && content.children.length > 1;
+      },
+      { timeout: 15000 }
+    );
+  }
+}
diff --git 
a/zeppelin-web-angular/e2e/tests/authentication/anonymous-login-redirect.spec.ts
 
b/zeppelin-web-angular/e2e/tests/authentication/anonymous-login-redirect.spec.ts
index ce66ce2f74..c123a48fb9 100644
--- 
a/zeppelin-web-angular/e2e/tests/authentication/anonymous-login-redirect.spec.ts
+++ 
b/zeppelin-web-angular/e2e/tests/authentication/anonymous-login-redirect.spec.ts
@@ -62,7 +62,7 @@ test.describe('Anonymous User Login Redirect', () => {
       await waitForZeppelinReady(page);
       await page.waitForURL(url => !url.toString().includes('#/login'));
 
-      await homePageUtil.verifyHomePageIntegrity();
+      await homePageUtil.verifyHomePageElements();
     });
 
     test('When clicking Zeppelin logo after redirect, Then should maintain 
home URL and content', async ({ page }) => {
@@ -83,7 +83,7 @@ test.describe('Anonymous User Login Redirect', () => {
       await waitForZeppelinReady(page);
       await page.waitForURL(url => !url.toString().includes('#/login'));
 
-      const metadata = await homePageUtil.getPageMetadata();
+      const metadata = await homePageUtil.getHomePageMetadata();
 
       expect(metadata.title).toContain('Zeppelin');
       expect(metadata.path).toContain('#/');
@@ -148,7 +148,7 @@ test.describe('Anonymous User Login Redirect', () => {
       await page.goto('/', { waitUntil: 'load' });
       await waitForZeppelinReady(page);
 
-      const homeMetadata = await homePageUtil.getPageMetadata();
+      const homeMetadata = await homePageUtil.getHomePageMetadata();
       expect(homeMetadata.path).toContain('#/');
       expect(homeMetadata.isAnonymous).toBe(true);
 
@@ -156,7 +156,7 @@ test.describe('Anonymous User Login Redirect', () => {
       await waitForZeppelinReady(page);
       await page.waitForURL(url => !url.toString().includes('#/login'));
 
-      const loginMetadata = await homePageUtil.getPageMetadata();
+      const loginMetadata = await homePageUtil.getHomePageMetadata();
       expect(loginMetadata.path).toContain('#/');
       expect(loginMetadata.path).not.toContain('#/login');
       expect(loginMetadata.isAnonymous).toBe(true);
diff --git a/zeppelin-web-angular/e2e/tests/home/home-page-elements.spec.ts 
b/zeppelin-web-angular/e2e/tests/home/home-page-elements.spec.ts
new file mode 100644
index 0000000000..f9f27d59e5
--- /dev/null
+++ b/zeppelin-web-angular/e2e/tests/home/home-page-elements.spec.ts
@@ -0,0 +1,172 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, test } from '@playwright/test';
+import { HomePage } from '../../models/home-page';
+import { HomePageUtil } from '../../models/home-page.util';
+import { addPageAnnotationBeforeEach, performLoginIfRequired, 
waitForZeppelinReady, PAGES } from '../../utils';
+
+test.describe('Home Page - Core Elements', () => {
+  addPageAnnotationBeforeEach(PAGES.WORKSPACE.HOME);
+
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/#/');
+    await waitForZeppelinReady(page);
+    await performLoginIfRequired(page);
+  });
+
+  test.describe('Welcome Section', () => {
+    test('should display welcome section with correct content', async ({ page 
}) => {
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        const homePage = new HomePage(page);
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When the page loads', async () => {
+        await waitForZeppelinReady(page);
+      });
+
+      await test.step('Then I should see the welcome section with correct 
content', async () => {
+        await homePageUtil.verifyWelcomeSection();
+      });
+    });
+
+    test('should have proper welcome message structure', async ({ page }) => {
+      const homePage = new HomePage(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the welcome section', async () => {
+        await expect(homePage.welcomeSection).toBeVisible();
+      });
+
+      await test.step('Then I should see the welcome heading', async () => {
+        await expect(homePage.welcomeHeading).toBeVisible();
+        const headingText = await homePage.getWelcomeHeadingText();
+        expect(headingText.trim()).toBe('Welcome to Zeppelin!');
+      });
+
+      await test.step('And I should see the welcome description', async () => {
+        await expect(homePage.welcomeDescription).toBeVisible();
+        const descriptionText = await homePage.getWelcomeDescriptionText();
+        expect(descriptionText).toContain('web-based notebook');
+        expect(descriptionText).toContain('interactive data analytics');
+      });
+    });
+  });
+
+  test.describe('Notebook Section', () => {
+    test('should display notebook section with all components', async ({ page 
}) => {
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        const homePage = new HomePage(page);
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I look for the notebook section', async () => {
+        await waitForZeppelinReady(page);
+      });
+
+      await test.step('Then I should see all notebook section components', 
async () => {
+        await homePageUtil.verifyNotebookSection();
+      });
+    });
+
+    test('should have functional refresh notes button', async ({ page }) => {
+      const homePage = new HomePage(page);
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page with notebook section 
visible', async () => {
+        await homePage.navigateToHome();
+        await expect(homePage.refreshNoteButton).toBeVisible();
+      });
+
+      await test.step('When I click the refresh notes button', async () => {
+        await homePage.clickRefreshNotes();
+      });
+
+      await test.step('Then the notebook list should still be visible', async 
() => {
+        await homePageUtil.verifyNotebookRefreshFunctionality();
+      });
+    });
+
+    test('should display notebook list component', async ({ page }) => {
+      const homePage = new HomePage(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I look for the notebook list', async () => {
+        await waitForZeppelinReady(page);
+      });
+
+      await test.step('Then I should see the notebook list component', async 
() => {
+        await expect(homePage.notebookList).toBeVisible();
+        const isVisible = await homePage.isNotebookListVisible();
+        expect(isVisible).toBe(true);
+      });
+    });
+  });
+
+  test.describe('Help Section', () => {
+    test('should display help section with documentation link', async ({ page 
}) => {
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        const homePage = new HomePage(page);
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I look for the help section', async () => {
+        await waitForZeppelinReady(page);
+      });
+
+      await test.step('Then I should see the help section', async () => {
+        await homePageUtil.verifyHelpSection();
+      });
+
+      await test.step('And I should see the documentation link', async () => {
+        const homePage = new HomePage(page);
+        await expect(homePage.externalLinks.documentation).toBeVisible();
+      });
+    });
+  });
+
+  test.describe('Community Section', () => {
+    test('should display community section with all links', async ({ page }) 
=> {
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        const homePage = new HomePage(page);
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I look for the community section', async () => {
+        await waitForZeppelinReady(page);
+      });
+
+      await test.step('Then I should see the community section', async () => {
+        await homePageUtil.verifyCommunitySection();
+      });
+
+      await test.step('And I should see all community links', async () => {
+        await homePageUtil.verifyExternalLinks();
+      });
+    });
+  });
+});
diff --git 
a/zeppelin-web-angular/e2e/tests/home/home-page-enhanced-functionality.spec.ts 
b/zeppelin-web-angular/e2e/tests/home/home-page-enhanced-functionality.spec.ts
new file mode 100644
index 0000000000..f6a93c725d
--- /dev/null
+++ 
b/zeppelin-web-angular/e2e/tests/home/home-page-enhanced-functionality.spec.ts
@@ -0,0 +1,64 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, test } from '@playwright/test';
+import { HomePageUtil } from '../../models/home-page.util';
+import { addPageAnnotationBeforeEach, performLoginIfRequired, 
waitForZeppelinReady, PAGES } from '../../utils';
+
+addPageAnnotationBeforeEach(PAGES.WORKSPACE.HOME);
+
+test.describe('Home Page Enhanced Functionality', () => {
+  let homeUtil: HomePageUtil;
+
+  test.beforeEach(async ({ page }) => {
+    homeUtil = new HomePageUtil(page);
+    await page.goto('/');
+    await waitForZeppelinReady(page);
+    await performLoginIfRequired(page);
+  });
+
+  test.describe('Given documentation links are displayed', () => {
+    test('When documentation link is checked Then should have correct version 
in URL', async ({ page }) => {
+      await homeUtil.verifyDocumentationVersionLink();
+    });
+
+    test('When external links are checked Then should all open in new tab', 
async ({ page }) => {
+      await homeUtil.verifyAllExternalLinksTargetBlank();
+    });
+  });
+
+  test.describe('Given welcome section display', () => {
+    test('When page loads Then should show welcome content with proper text', 
async ({ page }) => {
+      await homeUtil.verifyWelcomeSection();
+    });
+
+    test('When welcome section is displayed Then should contain interactive 
elements', async ({ page }) => {
+      await homeUtil.verifyNotebookSection();
+    });
+  });
+
+  test.describe('Given community section content', () => {
+    test('When community section loads Then should display help and community 
headings', async ({ page }) => {
+      await homeUtil.verifyHelpSection();
+      await homeUtil.verifyCommunitySection();
+    });
+
+    test('When external links are displayed Then should show correct targets', 
async ({ page }) => {
+      const linkTargets = await homeUtil.testExternalLinkTargets();
+
+      
expect(linkTargets.documentationHref).toContain('zeppelin.apache.org/docs');
+      expect(linkTargets.mailingListHref).toContain('community.html');
+      expect(linkTargets.issuesTrackingHref).toContain('issues.apache.org');
+      expect(linkTargets.githubHref).toContain('github.com/apache/zeppelin');
+    });
+  });
+});
diff --git 
a/zeppelin-web-angular/e2e/tests/home/home-page-external-links.spec.ts 
b/zeppelin-web-angular/e2e/tests/home/home-page-external-links.spec.ts
new file mode 100644
index 0000000000..34e7e27de0
--- /dev/null
+++ b/zeppelin-web-angular/e2e/tests/home/home-page-external-links.spec.ts
@@ -0,0 +1,169 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, test } from '@playwright/test';
+import { HomePage } from '../../models/home-page';
+import { HomePageUtil } from '../../models/home-page.util';
+import { addPageAnnotationBeforeEach, performLoginIfRequired, 
waitForZeppelinReady, PAGES } from '../../utils';
+
+test.describe('Home Page - External Links', () => {
+  addPageAnnotationBeforeEach(PAGES.WORKSPACE.HOME);
+
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/#/');
+    await waitForZeppelinReady(page);
+    await performLoginIfRequired(page);
+  });
+
+  test.describe('Documentation Link', () => {
+    test('should have correct documentation link with dynamic version', async 
({ page }) => {
+      const homePage = new HomePage(page);
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the documentation link', async () => {
+        await expect(homePage.externalLinks.documentation).toBeVisible();
+      });
+
+      await test.step('Then it should have the correct href pattern', async () 
=> {
+        const linkTargets = await homePageUtil.testExternalLinkTargets();
+        
expect(linkTargets.documentationHref).toContain('zeppelin.apache.org/docs');
+        expect(linkTargets.documentationHref).toContain('index.html');
+      });
+
+      await test.step('And it should open in a new tab', async () => {
+        const target = await 
homePage.externalLinks.documentation.getAttribute('target');
+        expect(target).toBe('_blank');
+      });
+    });
+  });
+
+  test.describe('Community Links', () => {
+    test('should have correct mailing list link', async ({ page }) => {
+      const homePage = new HomePage(page);
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the mailing list link', async () => {
+        await expect(homePage.externalLinks.mailingList).toBeVisible();
+      });
+
+      await test.step('Then it should have the correct href', async () => {
+        const linkTargets = await homePageUtil.testExternalLinkTargets();
+        
expect(linkTargets.mailingListHref).toBe('http://zeppelin.apache.org/community.html');
+      });
+
+      await test.step('And it should open in a new tab', async () => {
+        const target = await 
homePage.externalLinks.mailingList.getAttribute('target');
+        expect(target).toBe('_blank');
+      });
+
+      await test.step('And it should have the mail icon', async () => {
+        const mailIcon = 
homePage.externalLinks.mailingList.locator('i[nz-icon][nzType="mail"]');
+        await expect(mailIcon).toBeVisible();
+      });
+    });
+
+    test('should have correct issues tracking link', async ({ page }) => {
+      const homePage = new HomePage(page);
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the issues tracking link', async () => {
+        await expect(homePage.externalLinks.issuesTracking).toBeVisible();
+      });
+
+      await test.step('Then it should have the correct href', async () => {
+        const linkTargets = await homePageUtil.testExternalLinkTargets();
+        expect(linkTargets.issuesTrackingHref).toBe(
+          
'https://issues.apache.org/jira/projects/ZEPPELIN/issues/filter=allopenissues'
+        );
+      });
+
+      await test.step('And it should open in a new tab', async () => {
+        const target = await 
homePage.externalLinks.issuesTracking.getAttribute('target');
+        expect(target).toBe('_blank');
+      });
+
+      await test.step('And it should have the exception icon', async () => {
+        const exceptionIcon = 
homePage.externalLinks.issuesTracking.locator('i[nz-icon][nzType="exception"]');
+        await expect(exceptionIcon).toBeVisible();
+      });
+    });
+
+    test('should have correct GitHub link', async ({ page }) => {
+      const homePage = new HomePage(page);
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the GitHub link', async () => {
+        await expect(homePage.externalLinks.github).toBeVisible();
+      });
+
+      await test.step('Then it should have the correct href', async () => {
+        const linkTargets = await homePageUtil.testExternalLinkTargets();
+        
expect(linkTargets.githubHref).toBe('https://github.com/apache/zeppelin');
+      });
+
+      await test.step('And it should open in a new tab', async () => {
+        const target = await 
homePage.externalLinks.github.getAttribute('target');
+        expect(target).toBe('_blank');
+      });
+
+      await test.step('And it should have the GitHub icon', async () => {
+        const githubIcon = 
homePage.externalLinks.github.locator('i[nz-icon][nzType="github"]');
+        await expect(githubIcon).toBeVisible();
+      });
+    });
+  });
+
+  test.describe('Link Verification', () => {
+    test('should have all external links with proper attributes', async ({ 
page }) => {
+      const homePage = new HomePage(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine all external links', async () => {
+        await expect(homePage.externalLinks.documentation).toBeVisible();
+        await expect(homePage.externalLinks.mailingList).toBeVisible();
+        await expect(homePage.externalLinks.issuesTracking).toBeVisible();
+        await expect(homePage.externalLinks.github).toBeVisible();
+      });
+
+      await test.step('Then all links should open in new tabs', async () => {
+        const docTarget = await 
homePage.externalLinks.documentation.getAttribute('target');
+        const mailTarget = await 
homePage.externalLinks.mailingList.getAttribute('target');
+        const issuesTarget = await 
homePage.externalLinks.issuesTracking.getAttribute('target');
+        const githubTarget = await 
homePage.externalLinks.github.getAttribute('target');
+
+        expect(docTarget).toBe('_blank');
+        expect(mailTarget).toBe('_blank');
+        expect(issuesTarget).toBe('_blank');
+        expect(githubTarget).toBe('_blank');
+      });
+    });
+  });
+});
diff --git a/zeppelin-web-angular/e2e/tests/home/home-page-layout.spec.ts 
b/zeppelin-web-angular/e2e/tests/home/home-page-layout.spec.ts
new file mode 100644
index 0000000000..ef2698750c
--- /dev/null
+++ b/zeppelin-web-angular/e2e/tests/home/home-page-layout.spec.ts
@@ -0,0 +1,139 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, test } from '@playwright/test';
+import { HomePage } from '../../models/home-page';
+import { HomePageUtil } from '../../models/home-page.util';
+import { addPageAnnotationBeforeEach, performLoginIfRequired, 
waitForZeppelinReady, PAGES } from '../../utils';
+
+test.describe('Home Page - Layout and Grid', () => {
+  addPageAnnotationBeforeEach(PAGES.WORKSPACE.HOME);
+
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/#/');
+    await waitForZeppelinReady(page);
+    await performLoginIfRequired(page);
+  });
+
+  test.describe('Responsive Grid Layout', () => {
+    test('should display responsive grid structure', async ({ page }) => {
+      const homePageUtil = new HomePageUtil(page);
+
+      await test.step('Given I am on the home page', async () => {
+        const homePage = new HomePage(page);
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When the page loads', async () => {
+        await waitForZeppelinReady(page);
+      });
+    });
+
+    test('should have proper column distribution', async ({ page }) => {
+      const homePage = new HomePage(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the grid columns', async () => {
+        await expect(homePage.moreInfoGrid).toBeVisible();
+      });
+
+      await test.step('Then I should see the notebook column with proper 
sizing', async () => {
+        await expect(homePage.notebookColumn).toBeVisible();
+        // Check that the column contains notebook content
+        const notebookHeading = homePage.notebookColumn.locator('h3');
+        await expect(notebookHeading).toBeVisible();
+        const headingText = await notebookHeading.textContent();
+        expect(headingText).toContain('Notebook');
+      });
+
+      await test.step('And I should see the help/community column with proper 
sizing', async () => {
+        await expect(homePage.helpCommunityColumn).toBeVisible();
+        // Check that the column contains help and community content
+        const helpHeading = homePage.helpCommunityColumn.locator('h3').first();
+        await expect(helpHeading).toBeVisible();
+        const helpText = await helpHeading.textContent();
+        expect(helpText).toContain('Help');
+      });
+    });
+
+    test('should maintain layout structure across different viewport sizes', 
async ({ page }) => {
+      const homePage = new HomePage(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I resize to tablet view', async () => {
+        await page.setViewportSize({ width: 768, height: 1024 });
+        await page.waitForTimeout(500);
+      });
+
+      await test.step('Then the grid should still be visible and functional', 
async () => {
+        await expect(homePage.moreInfoGrid).toBeVisible();
+        await expect(homePage.notebookColumn).toBeVisible();
+        await expect(homePage.helpCommunityColumn).toBeVisible();
+      });
+
+      await test.step('When I resize to mobile view', async () => {
+        await page.setViewportSize({ width: 375, height: 667 });
+        await page.waitForTimeout(500);
+      });
+
+      await test.step('Then the grid should adapt to mobile layout', async () 
=> {
+        await expect(homePage.moreInfoGrid).toBeVisible();
+        await expect(homePage.notebookColumn).toBeVisible();
+        await expect(homePage.helpCommunityColumn).toBeVisible();
+
+        // Verify content is still accessible in mobile view
+        const notebookHeading = homePage.notebookColumn.locator('h3');
+        const helpHeading = homePage.helpCommunityColumn.locator('h3').first();
+        await expect(notebookHeading).toBeVisible();
+        await expect(helpHeading).toBeVisible();
+      });
+    });
+  });
+
+  test.describe('Content Organization', () => {
+    test('should organize content in logical sections', async ({ page }) => {
+      const homePage = new HomePage(page);
+
+      await test.step('Given I am on the home page', async () => {
+        await homePage.navigateToHome();
+      });
+
+      await test.step('When I examine the content organization', async () => {
+        await waitForZeppelinReady(page);
+      });
+
+      await test.step('Then I should see the welcome section at the top', 
async () => {
+        await expect(homePage.welcomeSection).toBeVisible();
+      });
+
+      await test.step('And I should see the notebook section in the left 
column', async () => {
+        const notebookInColumn = homePage.notebookColumn.locator('h3');
+        await expect(notebookInColumn).toBeVisible();
+        const text = await notebookInColumn.textContent();
+        expect(text).toContain('Notebook');
+      });
+
+      await test.step('And I should see help and community sections in the 
right column', async () => {
+        const helpHeading = 
homePage.helpCommunityColumn.locator('h3').filter({ hasText: 'Help' });
+        const communityHeading = 
homePage.helpCommunityColumn.locator('h3').filter({ hasText: 'Community' });
+        await expect(helpHeading).toBeVisible();
+        await expect(communityHeading).toBeVisible();
+      });
+    });
+  });
+});
diff --git 
a/zeppelin-web-angular/e2e/tests/home/home-page-note-operations.spec.ts 
b/zeppelin-web-angular/e2e/tests/home/home-page-note-operations.spec.ts
new file mode 100644
index 0000000000..f7be8fd9d6
--- /dev/null
+++ b/zeppelin-web-angular/e2e/tests/home/home-page-note-operations.spec.ts
@@ -0,0 +1,260 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, test } from '@playwright/test';
+import { HomePage } from '../../models/home-page';
+import { addPageAnnotationBeforeEach, performLoginIfRequired, 
waitForZeppelinReady, PAGES } from '../../utils';
+
+addPageAnnotationBeforeEach(PAGES.WORKSPACE.HOME);
+
+test.describe('Home Page Note Operations', () => {
+  let homePage: HomePage;
+
+  test.beforeEach(async ({ page }) => {
+    homePage = new HomePage(page);
+    await page.goto('/');
+    await waitForZeppelinReady(page);
+    await performLoginIfRequired(page);
+    await page.waitForSelector('zeppelin-node-list', { timeout: 15000 });
+  });
+
+  test.describe('Given note operations are available', () => {
+    test('When note list loads Then should show note action buttons on hover', 
async ({ page }) => {
+      const notesExist = await page.locator('.node .file').count();
+
+      if (notesExist > 0) {
+        const firstNote = page.locator('.node .file').first();
+        await firstNote.hover();
+
+        await expect(page.locator('.file .operation a[nztooltiptitle*="Rename 
note"]').first()).toBeVisible();
+        await expect(page.locator('.file .operation a[nztooltiptitle*="Clear 
output"]').first()).toBeVisible();
+        await expect(page.locator('.file .operation a[nztooltiptitle*="Move 
note to Trash"]').first()).toBeVisible();
+      } else {
+        console.log('No notes available for testing operations');
+      }
+    });
+
+    test('When hovering over note actions Then should show tooltip 
descriptions', async ({ page }) => {
+      const noteExists = await page
+        .locator('.node .file')
+        .first()
+        .isVisible()
+        .catch(() => false);
+
+      if (noteExists) {
+        const firstNote = page.locator('.node .file').first();
+        await firstNote.hover();
+
+        const renameIcon = page.locator('.file .operation 
a[nztooltiptitle*="Rename note"]').first();
+        const clearIcon = page.locator('.file .operation 
a[nztooltiptitle*="Clear output"]').first();
+        const deleteIcon = page.locator('.file .operation 
a[nztooltiptitle*="Move note to Trash"]').first();
+
+        await expect(renameIcon).toBeVisible();
+        await expect(clearIcon).toBeVisible();
+        await expect(deleteIcon).toBeVisible();
+
+        // Test tooltip visibility by hovering over each icon
+        await renameIcon.hover();
+        await expect(page.locator('.ant-tooltip', { hasText: 'Rename note' 
})).toBeVisible();
+
+        await clearIcon.hover();
+        await expect(page.locator('.ant-tooltip', { hasText: 'Clear output' 
})).toBeVisible();
+
+        await deleteIcon.hover();
+        await expect(page.locator('.ant-tooltip', { hasText: 'Move note to 
Trash' })).toBeVisible();
+      }
+    });
+  });
+
+  test.describe('Given rename note functionality', () => {
+    test('When rename button is clicked Then should trigger rename workflow', 
async ({ page }) => {
+      const noteExists = await page
+        .locator('.node .file')
+        .first()
+        .isVisible()
+        .catch(() => false);
+
+      if (noteExists) {
+        const noteItem = page.locator('.node .file').first();
+        await noteItem.hover();
+
+        const renameButton = page.locator('.file .operation 
a[nztooltiptitle*="Rename note"]').first();
+        await expect(renameButton).toBeVisible();
+        await renameButton.click();
+
+        await page
+          .waitForFunction(
+            () => {
+              return (
+                document.querySelector('zeppelin-note-rename') !== null ||
+                document.querySelector('[role="dialog"]') !== null ||
+                document.querySelector('.ant-modal') !== null
+              );
+            },
+            { timeout: 5000 }
+          )
+          .catch(() => {
+            console.log('Rename modal did not appear - might need different 
trigger');
+          });
+      }
+    });
+  });
+
+  test.describe('Given clear output functionality', () => {
+    test('When clear output button is clicked Then should show confirmation 
dialog', async ({ page }) => {
+      const noteExists = await page
+        .locator('.node .file')
+        .first()
+        .isVisible()
+        .catch(() => false);
+
+      if (noteExists) {
+        const noteItem = page.locator('.node .file').first();
+        await noteItem.hover();
+
+        const clearButton = page.locator('.file .operation 
a[nztooltiptitle*="Clear output"]').first();
+        await expect(clearButton).toBeVisible();
+        await clearButton.click();
+
+        await expect(page.locator('text=Do you want to clear all 
output?')).toBeVisible();
+      }
+    });
+
+    test('When clear output is confirmed Then should execute clear operation', 
async ({ page }) => {
+      const noteExists = await page
+        .locator('.node .file')
+        .first()
+        .isVisible()
+        .catch(() => false);
+
+      if (noteExists) {
+        const noteItem = page.locator('.node .file').first();
+        await noteItem.hover();
+
+        const clearButton = page.locator('.file .operation 
a[nztooltiptitle*="Clear output"]').first();
+        await expect(clearButton).toBeVisible();
+        await clearButton.click();
+
+        const confirmButton = page.locator('button:has-text("Yes")');
+        if (await confirmButton.isVisible()) {
+          await confirmButton.click();
+        }
+      }
+    });
+  });
+
+  test.describe('Given move to trash functionality', () => {
+    test('When delete button is clicked Then should show trash confirmation', 
async ({ page }) => {
+      const noteExists = await page
+        .locator('.node .file')
+        .first()
+        .isVisible()
+        .catch(() => false);
+
+      if (noteExists) {
+        const noteItem = page.locator('.node .file').first();
+        await noteItem.hover();
+
+        const deleteButton = page.locator('.file .operation 
a[nztooltiptitle*="Move note to Trash"]').first();
+        await expect(deleteButton).toBeVisible();
+        await deleteButton.click();
+
+        await expect(page.locator('text=This note will be moved to 
trash.')).toBeVisible();
+      }
+    });
+
+    test('When move to trash is confirmed Then should move note to trash 
folder', async ({ page }) => {
+      const noteExists = await page
+        .locator('.node .file')
+        .first()
+        .isVisible()
+        .catch(() => false);
+
+      if (noteExists) {
+        const noteItem = page.locator('.node .file').first();
+        await noteItem.hover();
+
+        const deleteButton = page.locator('.file .operation 
a[nztooltiptitle*="Move note to Trash"]').first();
+        await expect(deleteButton).toBeVisible();
+        await deleteButton.click();
+
+        const confirmButton = page.locator('button:has-text("Yes")');
+        if (await confirmButton.isVisible()) {
+          await confirmButton.click();
+
+          await page.waitForTimeout(2000);
+
+          const trashFolder = page.locator('.node .folder').filter({ hasText: 
'Trash' });
+          await expect(trashFolder).toBeVisible();
+        }
+      }
+    });
+  });
+
+  test.describe('Given trash folder operations', () => {
+    test('When trash folder exists Then should show restore and empty 
options', async ({ page }) => {
+      const trashExists = await page
+        .locator('.node .folder')
+        .filter({ hasText: 'Trash' })
+        .isVisible()
+        .catch(() => false);
+
+      if (trashExists) {
+        const trashFolder = page.locator('.node .folder').filter({ hasText: 
'Trash' });
+        await trashFolder.hover();
+
+        await expect(page.locator('.folder .operation 
a[nztooltiptitle*="Restore all"]')).toBeVisible();
+        await expect(page.locator('.folder .operation a[nztooltiptitle*="Empty 
all"]')).toBeVisible();
+      }
+    });
+
+    test('When restore all is clicked Then should show confirmation dialog', 
async ({ page }) => {
+      const trashExists = await page
+        .locator('.node .folder')
+        .filter({ hasText: 'Trash' })
+        .isVisible()
+        .catch(() => false);
+
+      if (trashExists) {
+        const trashFolder = page.locator('.node .folder').filter({ hasText: 
'Trash' });
+        await trashFolder.hover();
+
+        const restoreButton = page.locator('.folder .operation 
a[nztooltiptitle*="Restore all"]').first();
+        await expect(restoreButton).toBeVisible();
+        await restoreButton.click();
+
+        await expect(
+          page.locator('text=Folders and notes in the trash will be merged 
into their original position.')
+        ).toBeVisible();
+      }
+    });
+
+    test('When empty trash is clicked Then should show permanent deletion 
warning', async ({ page }) => {
+      const trashExists = await page
+        .locator('.node .folder')
+        .filter({ hasText: 'Trash' })
+        .isVisible()
+        .catch(() => false);
+
+      if (trashExists) {
+        const trashFolder = page.locator('.node .folder').filter({ hasText: 
'Trash' });
+        await trashFolder.hover();
+
+        const emptyButton = page.locator('.folder .operation 
a[nztooltiptitle*="Empty all"]').first();
+        await expect(emptyButton).toBeVisible();
+        await emptyButton.click();
+
+        await expect(page.locator('text=This cannot be undone. Are you 
sure?')).toBeVisible();
+      }
+    });
+  });
+});
diff --git 
a/zeppelin-web-angular/e2e/tests/home/home-page-notebook-actions.spec.ts 
b/zeppelin-web-angular/e2e/tests/home/home-page-notebook-actions.spec.ts
new file mode 100644
index 0000000000..c3e7e1388b
--- /dev/null
+++ b/zeppelin-web-angular/e2e/tests/home/home-page-notebook-actions.spec.ts
@@ -0,0 +1,68 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { expect, test } from '@playwright/test';
+import { HomePageUtil } from '../../models/home-page.util';
+import { addPageAnnotationBeforeEach, performLoginIfRequired, 
waitForZeppelinReady, PAGES } from '../../utils';
+
+addPageAnnotationBeforeEach(PAGES.WORKSPACE.HOME);
+
+test.describe('Home Page Notebook Actions', () => {
+  let homeUtil: HomePageUtil;
+
+  test.beforeEach(async ({ page }) => {
+    homeUtil = new HomePageUtil(page);
+    await page.goto('/');
+    await waitForZeppelinReady(page);
+    await performLoginIfRequired(page);
+  });
+
+  test.describe('Given notebook list is displayed', () => {
+    test('When page loads Then should show notebook actions', async ({ page }) 
=> {
+      await homeUtil.verifyNotebookActions();
+    });
+
+    test('When refresh button is clicked Then should trigger reload with 
loading state', async ({ page }) => {
+      await homeUtil.testNotebookRefreshLoadingState();
+    });
+
+    test('When filter is used Then should filter notebook list', async ({ page 
}) => {
+      await homeUtil.testFilterFunctionality('test');
+    });
+  });
+
+  test.describe('Given create new note action', () => {
+    test('When create new note is clicked Then should open note creation 
modal', async ({ page }) => {
+      try {
+        await homeUtil.verifyCreateNewNoteWorkflow();
+      } catch (error) {
+        console.log('Note creation modal might not appear immediately');
+      }
+    });
+  });
+
+  test.describe('Given import note action', () => {
+    test('When import note is clicked Then should open import modal', async ({ 
page }) => {
+      try {
+        await homeUtil.verifyImportNoteWorkflow();
+      } catch (error) {
+        console.log('Import modal might not appear immediately');
+      }
+    });
+  });
+
+  test.describe('Given notebook refresh functionality', () => {
+    test('When refresh is triggered Then should maintain notebook list 
visibility', async ({ page }) => {
+      await homeUtil.verifyNotebookRefreshFunctionality();
+    });
+  });
+});
diff --git a/zeppelin-web-angular/e2e/tests/workspace/workspace-main.spec.ts 
b/zeppelin-web-angular/e2e/tests/workspace/workspace-main.spec.ts
new file mode 100644
index 0000000000..c6292cbaec
--- /dev/null
+++ b/zeppelin-web-angular/e2e/tests/workspace/workspace-main.spec.ts
@@ -0,0 +1,69 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { test } from '@playwright/test';
+import { WorkspaceTestUtil } from '../../models/workspace-page.util';
+import { addPageAnnotationBeforeEach, PAGES } from '../../utils';
+
+addPageAnnotationBeforeEach(PAGES.WORKSPACE.MAIN);
+
+test.describe('Workspace Main Component', () => {
+  let workspaceUtil: WorkspaceTestUtil;
+
+  test.beforeEach(async ({ page }) => {
+    workspaceUtil = new WorkspaceTestUtil(page);
+  });
+
+  test.describe('Given user accesses workspace container', () => {
+    test('When workspace loads Then should display main container structure', 
async () => {
+      await workspaceUtil.navigateAndWaitForLoad();
+
+      await workspaceUtil.verifyWorkspaceLayout();
+      await workspaceUtil.verifyWorkspaceContainer();
+    });
+
+    test('When workspace loads Then should display header component', async () 
=> {
+      await workspaceUtil.navigateAndWaitForLoad();
+
+      await workspaceUtil.verifyHeaderVisibility(true);
+    });
+
+    test('When workspace loads Then should activate router outlet', async () 
=> {
+      await workspaceUtil.navigateAndWaitForLoad();
+
+      await workspaceUtil.verifyRouterOutletActivation();
+    });
+
+    test('When component activates Then should trigger onActivate event', 
async () => {
+      await workspaceUtil.navigateAndWaitForLoad();
+
+      await workspaceUtil.waitForComponentActivation();
+    });
+  });
+
+  test.describe('Given workspace header visibility', () => {
+    test('When not in publish mode Then should show header', async () => {
+      await workspaceUtil.navigateAndWaitForLoad();
+
+      await workspaceUtil.verifyHeaderVisibility(true);
+    });
+  });
+
+  test.describe('Given router outlet functionality', () => {
+    test('When navigating to workspace Then should load child components', 
async () => {
+      await workspaceUtil.navigateAndWaitForLoad();
+
+      await workspaceUtil.verifyRouterOutletActivation();
+      await workspaceUtil.waitForComponentActivation();
+    });
+  });
+});

Reply via email to