test(ui): remove onboarding e2e suite (#11605)

This commit is contained in:
Alejandro Bailo
2026-06-16 09:32:37 +02:00
committed by GitHub
parent b5bb85c956
commit dd1895d2c4
4 changed files with 0 additions and 840 deletions
-259
View File
@@ -1,259 +0,0 @@
import { Locator, Page, expect } from "@playwright/test";
import { BasePage } from "../base-page";
const TOUR_KEY_PREFIX = "prowler.tour";
// Keep in sync with addProviderTour.version (lib/tours/add-provider.tour.ts).
const ADD_PROVIDER_TOUR_VERSION = 2;
const ADD_PROVIDER_TOUR_KEY = `${TOUR_KEY_PREFIX}.add-provider.v${ADD_PROVIDER_TOUR_VERSION}`;
const CHECKPOINT_MARKER_KEY = "prowler.onboarding.checkpoint";
// One popover per active driver.js tour; used to assert single-fire / no auto-fire.
const DRIVER_POPOVER_SELECTOR = ".driver-popover";
// POM for onboarding flows. URL assertions live here; the spec only orchestrates actions.
// Note: the sequence store is ephemeral (no persist) — a fresh goto/reload resets it.
export class OnboardingPage extends BasePage {
readonly welcomeModal: Locator;
readonly getStartedButton: Locator;
readonly skipButton: Locator;
readonly checkpointDialog: Locator;
readonly checkpointContinueButton: Locator;
readonly checkpointFinishButton: Locator;
readonly addProviderTriggerAnchor: Locator;
readonly providerTypeAnchor: Locator;
readonly viewFirstScanLaunchAnchor: Locator;
readonly viewFirstScanTabsAnchor: Locator;
readonly exploreFindingsFiltersAnchor: Locator;
readonly exploreFindingsGroupAnchor: Locator;
readonly exploreFindingsResourcesAnchor: Locator;
readonly viewComplianceFrameworksAnchor: Locator;
readonly viewComplianceSearchAnchor: Locator;
readonly attackPathsIntroAnchor: Locator;
readonly attackPathsScanListAnchor: Locator;
readonly accountMenuTrigger: Locator;
readonly productTourSubTrigger: Locator;
readonly driverPopover: Locator;
constructor(page: Page) {
super(page);
this.welcomeModal = page.getByRole("dialog").filter({
has: page.getByRole("heading", { name: /Add your first provider/i }),
});
this.getStartedButton = page.getByRole("button", { name: "Get started" });
this.skipButton = page.getByRole("button", { name: "Skip for now" });
this.checkpointDialog = page.getByRole("dialog").filter({
has: page.getByRole("heading", {
name: /Provider added — keep exploring\?/i,
}),
});
this.checkpointContinueButton = page.getByRole("button", {
name: "Continue the tour",
});
this.checkpointFinishButton = page.getByRole("button", {
name: "Finish here",
});
this.addProviderTriggerAnchor = page.locator(
'[data-tour-id="add-provider-trigger"]',
);
this.providerTypeAnchor = page.locator(
'[data-tour-id="add-provider-provider-type"]',
);
this.viewFirstScanLaunchAnchor = page.locator(
'[data-tour-id="view-first-scan-launch"]',
);
this.viewFirstScanTabsAnchor = page.locator(
'[data-tour-id="view-first-scan-tabs"]',
);
this.exploreFindingsFiltersAnchor = page.locator(
'[data-tour-id="explore-findings-filters"]',
);
this.exploreFindingsGroupAnchor = page.locator(
'[data-tour-id="explore-findings-group"]',
);
this.exploreFindingsResourcesAnchor = page.locator(
'[data-tour-id="explore-findings-resources"]',
);
this.viewComplianceFrameworksAnchor = page.locator(
'[data-tour-id="view-compliance-frameworks"]',
);
this.viewComplianceSearchAnchor = page.locator(
'[data-tour-id="view-compliance-search"]',
);
this.attackPathsIntroAnchor = page.locator(
'[data-tour-id="attack-paths-intro"]',
);
this.attackPathsScanListAnchor = page.locator(
'[data-tour-id="attack-paths-scan-list"]',
);
this.accountMenuTrigger = page.getByRole("button", {
name: "Account menu",
});
this.productTourSubTrigger = page.getByRole("menuitem", {
name: "Product tour",
});
this.driverPopover = page.locator(DRIVER_POPOVER_SELECTOR);
}
// Clears all tour records and the checkpoint marker so the gate evaluates as a fresh browser.
async clearOnboardingState(): Promise<void> {
await this.page.evaluate(
({ prefix, checkpointKey }) => {
const keys: string[] = [];
for (let i = 0; i < window.localStorage.length; i++) {
const key = window.localStorage.key(i);
if (key && key.startsWith(prefix)) keys.push(key);
}
keys.forEach((key) => window.localStorage.removeItem(key));
window.localStorage.removeItem(checkpointKey);
},
{ prefix: TOUR_KEY_PREFIX, checkpointKey: CHECKPOINT_MARKER_KEY },
);
}
// Seeds a completed record so the restart path can prove the tour re-fires anyway.
async seedCompletedAddProviderRecord(): Promise<void> {
await this.page.evaluate(
({ key, version }) => {
window.localStorage.setItem(
key,
JSON.stringify({
tourId: "add-provider",
version,
state: "completed",
completedAt: new Date().toISOString(),
}),
);
},
{ key: ADD_PROVIDER_TOUR_KEY, version: ADD_PROVIDER_TOUR_VERSION },
);
}
async openAccountMenu(): Promise<void> {
await this.accountMenuTrigger.click();
}
// Hover the sub-trigger to open the per-flow submenu.
async openProductTourSubmenu(): Promise<void> {
await this.openAccountMenu();
await expect(this.productTourSubTrigger).toBeVisible();
await this.productTourSubTrigger.hover();
}
tourFlowMenuItem(title: string): Locator {
return this.page.getByRole("menuitem", { name: title, exact: true });
}
async selectTourFlow(title: string): Promise<void> {
await this.openProductTourSubmenu();
const item = this.tourFlowMenuItem(title);
await expect(item).toBeVisible();
await item.click();
}
async clickGetStarted(): Promise<void> {
await this.getStartedButton.click();
}
async clickCheckpointContinue(): Promise<void> {
await this.checkpointContinueButton.click();
}
async clickCheckpointFinish(): Promise<void> {
await this.checkpointFinishButton.click();
}
async closeActiveTour(): Promise<void> {
await this.page.keyboard.press("Escape");
}
async verifyWelcomeModalVisible(): Promise<void> {
await expect(this.welcomeModal).toBeVisible();
}
async verifyWelcomeModalNotVisible(): Promise<void> {
await expect(this.welcomeModal).not.toBeVisible();
}
async verifyCheckpointDialogVisible(): Promise<void> {
await expect(this.checkpointDialog).toBeVisible();
}
async verifyTriggerAnchorPresent(): Promise<void> {
await expect(this.addProviderTriggerAnchor).toBeVisible();
}
async verifyAnchorsPresent(): Promise<void> {
await expect(this.addProviderTriggerAnchor).toBeVisible();
await expect(this.providerTypeAnchor).toBeVisible();
}
async verifyViewFirstScanAnchorPresent(): Promise<void> {
await expect(this.viewFirstScanLaunchAnchor).toBeVisible();
}
async verifyExploreFindingsAnchorPresent(): Promise<void> {
await expect(this.exploreFindingsFiltersAnchor).toBeVisible();
}
async verifyViewComplianceAnchorPresent(): Promise<void> {
await expect(this.viewComplianceFrameworksAnchor).toBeVisible();
}
async verifyAttackPathsAnchorPresent(): Promise<void> {
await expect(this.attackPathsIntroAnchor).toBeVisible();
}
// OB-E2E-006: page owns the driver; onboarding must not mount a second one.
async verifySingleDriverPopover(): Promise<void> {
await expect(this.driverPopover).toHaveCount(1);
}
// OB-E2E-007/004: no tour auto-fires after a reload or stop.
async verifyNoDriverPopover(): Promise<void> {
await expect(this.driverPopover).toHaveCount(0);
}
async verifyOnProvidersPage(): Promise<void> {
await this.page.waitForURL(/\/providers(\?|$)/);
}
async verifyOnProvidersPageWithOnboardingParam(): Promise<void> {
await this.page.waitForURL(/\/providers\?onboarding=add-provider/);
}
async verifyOnScansPage(): Promise<void> {
await this.page.waitForURL(/\/scans(\?|$)/);
}
async verifyOnFindingsPage(): Promise<void> {
await this.page.waitForURL(/\/findings(\?|$)/);
}
async verifyOnFindingsReplayPage(): Promise<void> {
await this.page.waitForURL(/\/findings\?onboarding=explore-findings/);
}
async verifyOnCompliancePage(): Promise<void> {
await this.page.waitForURL(/\/compliance(\?|$)/);
}
async verifyOnAttackPathsPage(): Promise<void> {
await this.page.waitForURL(/\/attack-paths/);
}
async verifyStillOnFindingsPage(): Promise<void> {
await expect(this.page).toHaveURL(/\/findings/);
}
}
-334
View File
@@ -1,334 +0,0 @@
### E2E Tests: Onboarding System
**Suite ID:** `OB-E2E`
**Feature:** Extensible UI onboarding system: the guided add-provider tour, the
cross-route guided sequence after the first provider connects, and per-flow
manual replay from the avatar menu.
> Behavioral assertions only: Welcome modal visibility, checkpoint dialog
> visibility, navigation to each flow route, presence of the `data-tour-id`
> anchors in the DOM, and localStorage markers. Driver.js overlay animation is
> never asserted. Waits use `expect(...).toBeVisible()` and
> `page.waitForURL()` — never `networkidle`.
---
## Test Case: `OB-E2E-001` - Mandatory new-user onboarding path
**Priority:** `critical`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** A zero-provider authenticated user is forced into the
Welcome modal on first load; accepting it navigates to the add-provider flow and
exposes the tour trigger anchor.
**Preconditions:**
- Admin user authentication required (`admin.auth.setup`, reused `storageState`)
- All `prowler.tour.*` localStorage keys cleared before the test
- The account has zero providers (existing providers removed via `deleteProviderIfExists`)
### Flow Steps
1. Navigate to a page inside `app/(prowler)/`
2. Assert the Welcome modal is visible
3. Click "Get started"
4. Assert navigation to `/providers` (the `?onboarding=add-provider` param is consumed)
5. Assert the `data-tour-id="add-provider-trigger"` anchor is present in the DOM
### Expected Result
- Welcome modal is displayed for the zero-provider user
- Accepting navigates to the providers route
- The add-provider trigger anchor is mounted on the providers page
### Key verification points
- Welcome modal visible on first authenticated load (gate forced it)
- After accept, the URL is `/providers`
- `[data-tour-id="add-provider-trigger"]` exists in the DOM
### Notes
- Maps to spec: "Zero-provider user on first authenticated load", "User accepts
the Welcome modal", "Every tour step target has a matching data-tour-id anchor"
- Full execution requires the Prowler stack (API + DB + auth env). The trigger
anchor proves the tour surface is reachable without asserting overlay animation
---
## Test Case: `OB-E2E-002` - Restart onboarding from the avatar menu
**Priority:** `high`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** A user who already completed the tour can restart it
from the avatar menu; the tour starts again despite the existing completion
record.
**Preconditions:**
- Admin user authentication required (`admin.auth.setup`, reused `storageState`)
- A `completed` record exists in localStorage for `add-provider`
(`prowler.tour.add-provider.v2`)
### Flow Steps
1. Navigate to a page with the user nav visible
2. Open the avatar account menu
3. Click "Product tour"
4. Assert navigation to `/providers?onboarding=add-provider`
5. Assert the `data-tour-id="add-provider-trigger"` anchor is present in the DOM
### Expected Result
- The restart entry navigates to the add-provider flow route with the onboarding param
- The tour starts despite the existing completion record (no Welcome modal)
- The add-provider trigger anchor is mounted on the providers page
### Key verification points
- "Product tour" entry is present in the avatar menu
- After selecting it, the URL is `/providers?onboarding=add-provider`
- `[data-tour-id="add-provider-trigger"]` exists in the DOM (re-trigger bypassed the completion record)
### Notes
- Maps to spec: "User activates the restart entry point from the avatar menu",
"Re-trigger works after a full page reload", "Re-trigger does not depend on
prior browser state"
- Full execution requires the Prowler stack (API + DB + auth env)
---
## Test Case: `OB-E2E-003` - First-run guided sequence + checkpoint
**Priority:** `critical`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** After the first provider connects, the checkpoint
dialog offers a guided sequence; "Continue the tour" chains through scans,
findings, compliance, and attack paths, advancing only on tour completion.
**Preconditions:**
- Admin user authentication required (`admin.auth.setup`, reused `storageState`)
- All `prowler.tour.*` keys and the `prowler.onboarding.checkpoint` marker cleared
- A connected provider exists (a `false → true` `hasProviders` flip is reachable);
guarded/skipped when `E2E_AWS_PROVIDER_ACCOUNT_ID` is unset
### Flow Steps
1. Start zero-provider; assert the Welcome modal is visible
2. Connect a provider (real flip via `addAWSProvider`)
3. Assert the checkpoint dialog "Provider added — keep exploring?" is visible
4. Click "Continue the tour"
5. Assert `/scans` with `data-tour-id="view-first-scan-launch"` present
6. Complete the scans tour; assert `/findings` with `explore-findings-filters`
7. Complete; assert `/compliance` with `view-compliance-frameworks`
8. Complete; assert `/attack-paths` with `attack-paths-intro` present
### Expected Result
- The checkpoint fires once on the real provider-connected flip
- Continuing chains through each flow route in registry order
- Each route exposes its first anchor in the DOM
### Key verification points
- Checkpoint dialog visible after the provider connects
- Sequence visits `/scans → /findings → /compliance → /attack-paths`
- The route-specific anchor is present at each step
- `prowler.onboarding.checkpoint` marker is set after a choice
### Notes
- Maps to spec `onboarding-sequence`: "Checkpoint after first provider connects",
"Continue starts the sequence", "Next flow starts after navigating to its route"
- Full execution requires the Prowler stack (API + DB + auth env)
---
## Test Case: `OB-E2E-004` - Stop the sequence at any time
**Priority:** `high`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** Closing any tour mid-sequence ends the sequence; no
further flow auto-fires and a reload does not resume it.
**Preconditions:**
- Admin user authentication required (reused `storageState`)
- A connected provider exists; guarded/skipped when `E2E_AWS_PROVIDER_ACCOUNT_ID` is unset
- Tour state and checkpoint marker cleared in `beforeEach`
### Flow Steps
1. Start the guided sequence (continue from the checkpoint)
2. On `/findings`, close the active tour (press Escape)
3. Wait briefly and assert the URL is still `/findings` (no advance to `/compliance`)
4. Reload the page
5. Assert no tour auto-fires (no `.driver-popover` in the DOM)
### Expected Result
- Closing the tour stops the sequence immediately
- No navigation to `/compliance` occurs
- A reload does not resume the sequence
### Key verification points
- URL remains `/findings` after Escape (no auto-advance)
- After reload, zero `.driver-popover` elements exist
### Notes
- Maps to spec `onboarding-sequence`: "Stopping any tour ends the sequence"
- Full execution requires the Prowler stack (API + DB + auth env)
---
## Test Case: `OB-E2E-005` - Manual single-flow replay from the avatar menu
**Priority:** `high`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** The avatar "Product tour" submenu lists every flow;
selecting one replays that single flow only and never chains into the sequence.
**Preconditions:**
- Admin user authentication required (reused `storageState`)
- `completed` records seeded for the flows so the list represents replay state
### Flow Steps
1. Open the avatar account menu
2. Open the "Product tour" submenu
3. Assert all five flow titles are listed (registry order)
4. Select "Explore your findings"
5. Assert navigation to `/findings?onboarding=explore-findings`
6. Assert `data-tour-id="explore-findings-filters"` present
7. Close the tour and assert no navigation to `/compliance` (no sequence chaining)
### Expected Result
- The submenu lists all five flows by title
- Selecting a flow replays it standalone with the `?onboarding=<id>` param
- Closing the replayed tour does not advance to the next flow
### Key verification points
- Submenu contains `Add your first provider`, `Run your first scan`,
`Explore your findings`, `Check compliance`, `Visualize attack paths`
- URL is `/findings?onboarding=explore-findings`
- No advance to `/compliance` after closing
### Notes
- Maps to spec `onboarding` (MODIFIED): "Avatar entry opens an ordered list of
flows", "Selecting a flow replays that single flow only"
- Full execution requires the Prowler stack (API + DB + auth env)
---
## Test Case: `OB-E2E-006` - Attack-paths single-fire
**Priority:** `high`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** On `/attack-paths` only one driver popover exists at a
time — the page owns the driver and onboarding never mounts a second runner.
**Preconditions:**
- Admin user authentication required (reused `storageState`)
- A connected provider with at least one ready scan exists; guarded/skipped when
`E2E_AWS_PROVIDER_ACCOUNT_ID` is unset
### Flow Steps
1. Navigate to `/attack-paths?onboarding=attack-paths`
2. Wait for the attack-paths tour popover to appear
3. Count `.driver-popover` elements in the DOM
### Expected Result
- Exactly one driver popover exists (no double-fire)
### Key verification points
- `.driver-popover` count is exactly `1`
### Notes
- Maps to spec `onboarding-sequence`: "Attack-Paths Single-Fire Integration"
- Full execution requires the Prowler stack (API + DB + auth env)
---
## Test Case: `OB-E2E-007` - Refresh mid-sequence does not re-fire
**Priority:** `high`
**Tags:**
- type → @e2e
- feature → @onboarding
**Description/Objective:** The sequence slice is ephemeral; a hard reload mid-tour
resets it so no tour auto-fires, and no Welcome modal appears (provider connected).
**Preconditions:**
- Admin user authentication required (reused `storageState`)
- A connected provider exists; guarded/skipped when `E2E_AWS_PROVIDER_ACCOUNT_ID` is unset
- Tour state and checkpoint marker cleared in `beforeEach`
### Flow Steps
1. Start the guided sequence and land on `/scans`
2. Hard-reload the page
3. Assert no `.driver-popover` auto-fires
4. Assert the Welcome modal is not visible
### Expected Result
- No tour auto-fires after the reload (ephemeral slice reset)
- No Welcome modal (the provider is connected, so the gate stays closed)
### Key verification points
- Zero `.driver-popover` elements after reload
- Welcome modal is not visible
### Notes
- Maps to spec `onboarding-sequence`: "Refresh mid-sequence does not re-fire
infinitely"
- Full execution requires the Prowler stack (API + DB + auth env)
-245
View File
@@ -1,245 +0,0 @@
import { test } from "@playwright/test";
import { addAWSProvider, deleteProviderIfExists } from "../helpers";
import { ProvidersPage } from "../providers/providers-page";
import { OnboardingPage } from "./onboarding-page";
// Real AWS credentials for the hasProviders false→true flip; tests skip when unset.
const accountId = process.env.E2E_AWS_PROVIDER_ACCOUNT_ID ?? "";
const accessKey = process.env.E2E_AWS_PROVIDER_ACCESS_KEY ?? "";
const secretKey = process.env.E2E_AWS_PROVIDER_SECRET_KEY ?? "";
const hasAwsCredentials = Boolean(accountId && accessKey && secretKey);
// Guided onboarding is a Prowler Cloud-only feature; it never mounts in OSS.
const isCloudEnv = process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true";
test.describe("Onboarding", () => {
test.skip(
!isCloudEnv,
"Guided onboarding is a Prowler Cloud-only feature (NEXT_PUBLIC_IS_CLOUD_ENV != true)",
);
test.use({ storageState: "playwright/.auth/admin_user.json" });
test.describe("Mandatory new-user path", () => {
test.beforeEach(async ({ page }) => {
const providersPage = new ProvidersPage(page);
if (accountId) {
await deleteProviderIfExists(providersPage, accountId);
}
await providersPage.goto();
const onboardingPage = new OnboardingPage(page);
await onboardingPage.clearOnboardingState();
});
test(
"forces the Welcome modal and hands off to the add-provider tour",
{
tag: ["@critical", "@e2e", "@onboarding", "@OB-E2E-001"],
},
async ({ page }) => {
test.skip(
!accountId,
"E2E_AWS_PROVIDER_ACCOUNT_ID is not set; cannot guarantee a zero-provider account",
);
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await onboardingPage.verifyWelcomeModalVisible();
await onboardingPage.clickGetStarted();
await onboardingPage.verifyOnProvidersPage();
await onboardingPage.verifyTriggerAnchorPresent();
},
);
});
test.describe("Restart path", () => {
test.beforeEach(async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await onboardingPage.seedCompletedAddProviderRecord();
});
test(
"restarts the tour from the avatar menu despite a completion record",
{
tag: ["@high", "@e2e", "@onboarding", "@OB-E2E-002"],
},
async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await onboardingPage.selectTourFlow("Add your first provider");
await onboardingPage.verifyOnProvidersPageWithOnboardingParam();
await onboardingPage.verifyTriggerAnchorPresent();
},
);
});
test.describe("Guided sequence", () => {
test.beforeEach(async ({ page }) => {
const providersPage = new ProvidersPage(page);
if (accountId) {
await deleteProviderIfExists(providersPage, accountId);
}
await providersPage.goto();
const onboardingPage = new OnboardingPage(page);
await onboardingPage.clearOnboardingState();
});
test(
"runs the first-run sequence after the checkpoint and chains every flow",
{
tag: ["@critical", "@e2e", "@onboarding", "@OB-E2E-003"],
},
async ({ page }) => {
test.skip(
!hasAwsCredentials,
"E2E AWS provider credentials are not set; cannot drive a real hasProviders flip",
);
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await onboardingPage.verifyWelcomeModalVisible();
// Connect a provider to drive the genuine hasProviders false→true flip.
await addAWSProvider(page, accountId, accessKey, secretKey);
await onboardingPage.verifyCheckpointDialogVisible();
await onboardingPage.clickCheckpointContinue();
await onboardingPage.verifyOnScansPage();
await onboardingPage.verifyViewFirstScanAnchorPresent();
await onboardingPage.closeActiveTour();
await onboardingPage.verifyExploreFindingsAnchorPresent();
await onboardingPage.goto("/compliance");
await onboardingPage.verifyViewComplianceAnchorPresent();
await onboardingPage.goto("/attack-paths");
await onboardingPage.verifyAttackPathsAnchorPresent();
},
);
test(
"stops the sequence when a tour is closed and does not resume on reload",
{
tag: ["@high", "@e2e", "@onboarding", "@OB-E2E-004"],
},
async ({ page }) => {
test.skip(
!hasAwsCredentials,
"E2E AWS provider credentials are not set; cannot drive the guided sequence",
);
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await addAWSProvider(page, accountId, accessKey, secretKey);
await onboardingPage.verifyCheckpointDialogVisible();
await onboardingPage.clickCheckpointContinue();
await onboardingPage.verifyOnScansPage();
await onboardingPage.closeActiveTour();
await onboardingPage.verifyExploreFindingsAnchorPresent();
await onboardingPage.closeActiveTour();
// Closing the tour stops the sequence — no auto-advance to compliance.
await onboardingPage.verifyStillOnFindingsPage();
// Ephemeral sequence must not resume after a hard reload.
await onboardingPage.refresh();
await onboardingPage.verifyNoDriverPopover();
},
);
test(
"does not re-fire after a hard reload mid-sequence",
{
tag: ["@high", "@e2e", "@onboarding", "@OB-E2E-007"],
},
async ({ page }) => {
test.skip(
!hasAwsCredentials,
"E2E AWS provider credentials are not set; cannot drive the guided sequence",
);
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await addAWSProvider(page, accountId, accessKey, secretKey);
await onboardingPage.verifyCheckpointDialogVisible();
await onboardingPage.clickCheckpointContinue();
await onboardingPage.verifyOnScansPage();
// Hard reload resets the ephemeral slice; provider is connected so the gate stays silent.
await onboardingPage.refresh();
await onboardingPage.verifyNoDriverPopover();
await onboardingPage.verifyWelcomeModalNotVisible();
},
);
});
test.describe("Manual replay", () => {
test.beforeEach(async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await onboardingPage.seedCompletedAddProviderRecord();
});
test(
"replays a single flow from the avatar submenu without chaining",
{
tag: ["@high", "@e2e", "@onboarding", "@OB-E2E-005"],
},
async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/providers");
await onboardingPage.openProductTourSubmenu();
for (const title of [
"Add your first provider",
"Run your first scan",
"Explore your findings",
"Check compliance",
"Visualize attack paths",
]) {
await onboardingPage.verifyElementVisible(
onboardingPage.tourFlowMenuItem(title),
);
}
await onboardingPage.tourFlowMenuItem("Explore your findings").click();
await onboardingPage.verifyOnFindingsReplayPage();
await onboardingPage.verifyExploreFindingsAnchorPresent();
// Replay must not chain to the next flow on close.
await onboardingPage.closeActiveTour();
await onboardingPage.verifyStillOnFindingsPage();
},
);
});
test.describe("Attack-paths single-fire", () => {
test(
"renders exactly one driver popover on the attack-paths route",
{
tag: ["@high", "@e2e", "@onboarding", "@OB-E2E-006"],
},
async ({ page }) => {
test.skip(
!hasAwsCredentials,
"E2E AWS provider credentials are not set; attack-paths needs a ready scan",
);
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto("/attack-paths?onboarding=attack-paths");
await onboardingPage.verifyOnAttackPathsPage();
await onboardingPage.verifySingleDriverPopover();
},
);
});
});