diff --git a/tests/e2e/baseTest.ts b/tests/e2e/baseTest.ts index c07adbd47..0ee509aa9 100644 --- a/tests/e2e/baseTest.ts +++ b/tests/e2e/baseTest.ts @@ -6,7 +6,6 @@ import { createWriteStream, existsSync, mkdtempSync, readFileSync } from "fs"; import { tmpdir } from "os"; import path from "path"; import { DEBUG_LOGGING_ENABLED } from "./constants"; -import { Notification } from "./objects/notifications/Notification"; import { NotificationArea } from "./objects/notifications/NotificationArea"; import { Quickpick } from "./objects/quickInputs/Quickpick"; import { @@ -29,8 +28,8 @@ import { } from "./utils/connections"; import { produceMessages } from "./utils/producer"; import { configureVSCodeSettings } from "./utils/settings"; -import { openConfluentSidebar } from "./utils/sidebarNavigation"; import { randomHexString } from "./utils/strings"; +import { openConfluentSidebar, prepareTestWorkspace } from "./utils/workspace"; // NOTE: we can't import these two directly from 'global.setup.ts' // cached test setup file path that's shared across worker processes @@ -365,25 +364,8 @@ async function globalBeforeEach(page: Page, testTempDir: string): Promise // make sure settings are set to defaults for each test configureVSCodeSettings(testTempDir); - // dismiss the "All installed extensions are temporarily disabled" notification that will - // always appear since we launch with --disable-extensions - const notificationArea = new NotificationArea(page); - const infoNotifications = notificationArea.infoNotifications.filter({ - hasText: "All installed extensions are temporarily disabled", - }); - await expect(infoNotifications).not.toHaveCount(0); - const notification = new Notification(page, infoNotifications.first()); - await notification.dismiss(); - - // collapse the secondary sidebar if it's expanded since it isn't used for anything - try { - await expect(page.locator(`[id="workbench.parts.auxiliarybar"]`)).toBeVisible({ - timeout: 1000, - }); - await executeVSCodeCommand(page, "View: Toggle Secondary Side Bar Visibility"); - } catch { - console.warn("Error locating/toggling secondary sidebar"); - } + // dismiss the disabled-extensions toast and collapse the secondary sidebar + await prepareTestWorkspace(page); } async function globalAfterEach( diff --git a/tests/e2e/specs/confluent.spec.ts b/tests/e2e/specs/confluent.spec.ts index bc26cd91e..dbd764346 100644 --- a/tests/e2e/specs/confluent.spec.ts +++ b/tests/e2e/specs/confluent.spec.ts @@ -5,7 +5,7 @@ import { ResourcesView } from "../objects/views/ResourcesView"; import { CCloudConnectionItem } from "../objects/views/viewItems/CCloudConnectionItem"; import { Tag } from "../tags"; import { NOT_CONNECTED_TEXT, setupCCloudConnection } from "../utils/connections"; -import { openConfluentSidebar } from "../utils/sidebarNavigation"; +import { openConfluentSidebar } from "../utils/workspace"; test.describe(() => { test("should activate the extension", { tag: [Tag.Smoke] }, async ({ page }) => { diff --git a/tests/e2e/specs/flinkArtifact.spec.ts b/tests/e2e/specs/flinkArtifact.spec.ts index 259b1f48e..034306b95 100644 --- a/tests/e2e/specs/flinkArtifact.spec.ts +++ b/tests/e2e/specs/flinkArtifact.spec.ts @@ -14,8 +14,8 @@ import { Tag } from "../tags"; import { ConnectionType } from "../types/connection"; import { executeVSCodeCommand } from "../utils/commands"; import { createInvalidJarFile, createLargeFile } from "../utils/flinkDatabase"; -import { openConfluentSidebar } from "../utils/sidebarNavigation"; import { randomHexString } from "../utils/strings"; +import { openConfluentSidebar, prepareTestWorkspace } from "../utils/workspace"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -194,8 +194,7 @@ test.describe("Flink Artifacts", { tag: [Tag.CCloud, Tag.FlinkArtifacts] }, () = // wait for the window to finish transitioning to the new workspace before // trying to open the command palette again - await page.locator(".monaco-workbench").waitFor(); - await page.waitForLoadState("domcontentloaded"); + await prepareTestWorkspace(page); // make sure the explorer view is visible before we activate the extension await executeVSCodeCommand(page, "workbench.view.explorer"); diff --git a/tests/e2e/utils/connections.ts b/tests/e2e/utils/connections.ts index c6c82bac4..e90bd0561 100644 --- a/tests/e2e/utils/connections.ts +++ b/tests/e2e/utils/connections.ts @@ -16,7 +16,7 @@ import { LocalConnectionItem } from "../objects/views/viewItems/LocalConnectionI import type { DirectConnectionForm } from "../objects/webviews/DirectConnectionFormWebview"; import type { DirectConnectionOptions, LocalConnectionOptions } from "../types/connection"; import { executeVSCodeCommand } from "./commands"; -import { openConfluentSidebar } from "./sidebarNavigation"; +import { openConfluentSidebar } from "./workspace"; export const CCLOUD_SIGNIN_URL_PATH = join(tmpdir(), "vscode-e2e-ccloud-signin-url.txt"); export const NOT_CONNECTED_TEXT = "(No connection)"; diff --git a/tests/e2e/utils/sidebarNavigation.ts b/tests/e2e/utils/sidebarNavigation.ts deleted file mode 100644 index 1ce13eff9..000000000 --- a/tests/e2e/utils/sidebarNavigation.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Page } from "@playwright/test"; -import { expect } from "@playwright/test"; -import { ViewContainer } from "../objects/ViewContainer"; -import { ResourcesView } from "../objects/views/ResourcesView"; -import { executeVSCodeCommand } from "./commands"; - -/** - * Opens the primary sidebar with the main Confluent - * {@link https://code.visualstudio.com/api/ux-guidelines/views#view-containers view container}, - * which activates the extension if it isn't already activated. - * - * For most tests, this should be done first unless another explicit extension activation event is - * performed. - */ -export async function openConfluentSidebar(page: Page): Promise { - await page.waitForLoadState("domcontentloaded"); - - // check if the view container is already visible in the primary sidebar first - // and if not, use the VS Code focus command to show it - const viewContainer = new ViewContainer(page, "confluent"); - try { - await expect(viewContainer.locator).toBeVisible({ timeout: 2000 }); - } catch { - // use the VS Code command rather than clicking the activity bar tab directly, - // because the tab click toggles the sidebar: if the Confluent tab is already - // active (but hasn't rendered yet), clicking it closes the sidebar instead of opening it - await executeVSCodeCommand(page, "confluent-resources.focus"); - await expect(viewContainer.locator).toBeVisible(); - } - - const resourcesView = new ResourcesView(page); - // the Resources should be visible and expanded by default - await expect(resourcesView.header).toHaveAttribute("aria-expanded", "true"); - // and should show the "Confluent Cloud" placeholder item (not "No resources found") - await expect(resourcesView.confluentCloudItem).toBeVisible(); - // we don't check for the "Local" item here in the event the Confluent Cloud item has children - // and is expanded, because it may push the Local item out of view and adding logic in here to - // scroll to it would be more complex than necessary for this utility function -} diff --git a/tests/e2e/utils/workspace.ts b/tests/e2e/utils/workspace.ts new file mode 100644 index 000000000..be30fe9a6 --- /dev/null +++ b/tests/e2e/utils/workspace.ts @@ -0,0 +1,82 @@ +import type { Page } from "@playwright/test"; +import { expect } from "@playwright/test"; +import { Notification } from "../objects/notifications/Notification"; +import { NotificationArea } from "../objects/notifications/NotificationArea"; +import { ViewContainer } from "../objects/ViewContainer"; +import { ResourcesView } from "../objects/views/ResourcesView"; +import { executeVSCodeCommand } from "./commands"; + +/** + * Prepare a freshly-loaded VS Code workspace/window before any test interactions. + * + * NOTE: This should be safe to call at fixture startup (before any test has touched the window) and + * after any workspace/window reload. + */ +export async function prepareTestWorkspace(page: Page): Promise { + // wait for the (new) VS Code window DOM + workbench shell to be ready; same waits the + // electronApp fixture uses at initial launch + await page.waitForLoadState("domcontentloaded"); + await page.locator(".monaco-workbench").waitFor({ timeout: 30000 }); + // any locator-based interactions below this point will naturally wait for the Electron DOM to be + // ready/visible/etc, so we don't need additional waits for that here + + // dismiss the "All installed extensions are temporarily disabled" toast that always appears + // under --disable-extensions; tolerate it being absent on subsequent reloads + try { + const notificationArea = new NotificationArea(page); + const infoNotifications = notificationArea.infoNotifications.filter({ + hasText: "All installed extensions are temporarily disabled", + }); + await expect(infoNotifications).not.toHaveCount(0, { timeout: 2000 }); + await new Notification(page, infoNotifications.first()).dismiss(); + } catch { + // toast wasn't present or couldn't be dismissed + } + + // collapse the secondary sidebar if it's expanded since it isn't used for anything + try { + await expect(page.locator(`[id="workbench.parts.auxiliarybar"]`)).toBeVisible({ + timeout: 1000, + }); + // use the default VS Code keybinding instead of the command palette: `Ctrl+Shift+P` can be + // swallowed by a focused Chat webview on workspace reload, but `Ctrl+Alt+B` is a global + // workbench keybinding that routes regardless of webview focus + await page.keyboard.press("ControlOrMeta+Alt+B"); + } catch { + console.warn("Error locating/toggling secondary sidebar"); + } +} + +/** + * Opens the primary sidebar with the main Confluent + * {@link https://code.visualstudio.com/api/ux-guidelines/views#view-containers view container}, + * which activates the extension if it isn't already activated. + * + * For most tests, this should be done first unless another explicit extension activation event is + * performed. + */ +export async function openConfluentSidebar(page: Page): Promise { + await page.waitForLoadState("domcontentloaded"); + + // check if the view container is already visible in the primary sidebar first + // and if not, use the VS Code focus command to show it + const viewContainer = new ViewContainer(page, "confluent"); + try { + await expect(viewContainer.locator).toBeVisible({ timeout: 2000 }); + } catch { + // use the VS Code command rather than clicking the activity bar tab directly, + // because the tab click toggles the sidebar: if the Confluent tab is already + // active (but hasn't rendered yet), clicking it closes the sidebar instead of opening it + await executeVSCodeCommand(page, "confluent-resources.focus"); + await expect(viewContainer.locator).toBeVisible(); + } + + const resourcesView = new ResourcesView(page); + // the Resources should be visible and expanded by default + await expect(resourcesView.header).toHaveAttribute("aria-expanded", "true"); + // and should show the "Confluent Cloud" placeholder item (not "No resources found") + await expect(resourcesView.confluentCloudItem).toBeVisible(); + // we don't check for the "Local" item here in the event the Confluent Cloud item has children + // and is expanded, because it may push the Local item out of view and adding logic in here to + // scroll to it would be more complex than necessary for this utility function +}