Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions deploy/devsandbox-dashboard/ui-e2e-tests/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
SSO_USERNAME=${SSO_USERNAME}
SSO_PASSWORD=${SSO_PASSWORD}
BASE_URL=${BASE_URL}
ENVIRONMENT=ui-e2e-tests
BROWSER=${BROWSER}
ENVIRONMENT=${ENVIRONMENT}
BROWSER=${BROWSER}
KUBECONFIG=${KUBECONFIG}
33 changes: 31 additions & 2 deletions make/devsandbox-dashboard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ e2e-run-devsandbox-dashboard:
@echo "Running Developer Sandbox Dashboard setup e2e tests..."
DEVSANDBOX_DASHBOARD_NS=${DEVSANDBOX_DASHBOARD_NS} go test "./test/e2e/devsandbox-dashboard/setup" -v -timeout=10m -failfast

@echo "Running Developer Sandbox Dashboard e2e tests in firefox..."
@SSO_USERNAME=$(SSO_USERNAME_READ) SSO_PASSWORD=$(SSO_PASSWORD_READ) BASE_URL=${RHDH} BROWSER=firefox envsubst < deploy/devsandbox-dashboard/ui-e2e-tests/.env > testsupport/devsandbox-dashboard/.env
@echo "Running Developer Sandbox Dashboard e2e tests in Firefox..."
@SSO_USERNAME=$(SSO_USERNAME_READ) SSO_PASSWORD=$(SSO_PASSWORD_READ) BASE_URL=${RHDH} BROWSER=firefox ENVIRONMENT=${UI_ENVIRONMENT} envsubst < deploy/devsandbox-dashboard/ui-e2e-tests/.env > testsupport/devsandbox-dashboard/.env
@HOST_NS=$(HOST_NS) MEMBER_NS=$(MEMBER_NS) MEMBER_NS_2=$(MEMBER_NS_2) REGISTRATION_SERVICE_NS=$(HOST_NS) SECOND_MEMBER_MODE=$(SECOND_MEMBER) go test "./test/e2e/devsandbox-dashboard" -v -timeout=10m -failfast

@echo "The Developer Sandbox Dashboard e2e tests successfully finished"
Expand Down Expand Up @@ -99,3 +99,32 @@ endif
-e PUBLISH_UI=false \
-e RUNNING_IN_CONTAINER=true \
$(E2E_TEST_IMAGE_NAME) make test-devsandbox-dashboard-e2e

# Run Developer Sandbox Dashboard e2e tests against prod
.PHONY: test-devsandbox-dashboard-e2e-prod
test-devsandbox-dashboard-e2e-prod: ksctl
@echo "Installing Firefox browser for Playwright..."
go tool playwright install firefox

@echo "Running Developer Sandbox Dashboard e2e tests in Firefox..."
@SSO_USERNAME=${SSO_USERNAME_READ} SSO_PASSWORD=${SSO_PASSWORD_READ} BASE_URL=https://sandbox.redhat.com/ BROWSER=firefox ENVIRONMENT=prod KUBECONFIG=${KUBECONFIG} envsubst < deploy/devsandbox-dashboard/ui-e2e-tests/.env > testsupport/devsandbox-dashboard/.env
@go test "./test/e2e/devsandbox-dashboard" -v -timeout=10m -failfast

@echo "The Developer Sandbox Dashboard e2e tests successfully finished"


# Run Developer Sandbox Dashboard e2e tests against prod in container using podman
.PHONY: test-devsandbox-dashboard-in-container-prod
test-devsandbox-dashboard-in-container-prod: build-devsandbox-dashboard-e2e-tests
@rm -f build/_output/bin/ksctl
@echo "running the prod e2e tests in podman container..."
podman run --platform $(IMAGE_PLATFORM) --rm \
-v $(KUBECONFIG):/root/.kube/config \
-e KUBECONFIG=/root/.kube/config \
-v ${PWD}:/root/toolchain-e2e \
-e E2E_REPO_PATH=/root/toolchain-e2e \
-e SSO_USERNAME=$(SSO_USERNAME) \
-e SSO_PASSWORD=$(SSO_PASSWORD) \
-e RUNNING_IN_CONTAINER=true \
-e PATH=/app/build/_output/bin:$$PATH \
$(E2E_TEST_IMAGE_NAME) make test-devsandbox-dashboard-e2e-prod
7 changes: 7 additions & 0 deletions test/e2e/devsandbox-dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ If you want to use your local devsandbox-dashboard, please:
1. Set the `QUAY_NAMESPACE` environment variable to your quay username: `export QUAY_NAMESPACE=<your-quay-username>`
2. Run `make test-devsandbox-dashboard-in-container SSO_USERNAME=<SSO_USERNAME> SSO_PASSWORD=<SSO_PASSWORD> UI_REPO_PATH=${PWD}/../devsandbox-dashboard`

## Deploy Developer Sandbox Dashboard in Prod

`make test-devsandbox-dashboard-e2e-prod SSO_USERNAME=${SSO_USERNAME} SSO_PASSWORD=${SSO_PASSWORD} KUBECONFIG=${KUBECONFIG}`

The Developer Sandbox Dashboard E2E tests will run against the production environment at `https://sandbox.redhat.com/`. Note that this requires `ksctl` to manage usersignups in production.


## Deploy Developer Sandbox Dashboard in E2E mode

Please note that OCP cluster does not have a valid CA, so when accessing the Developer Sandbox Dashboard, you need to:
Expand Down
75 changes: 57 additions & 18 deletions test/e2e/devsandbox-dashboard/fresh_signup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"regexp"
"strings"
"testing"

"github.com/codeready-toolchain/toolchain-e2e/testsupport"
Expand Down Expand Up @@ -40,7 +41,8 @@ func TestFreshSignup(t *testing.T) {
}

func ensureNoUserSignup(t *testing.T, env, username string) {
if env == sandboxui.TestEnv {
switch env {
case sandboxui.TestEnv:
awaitilities := testsupport.WaitForDeployments(t)
hostAwait := awaitilities.Host()
userSignup, err := sandboxui.WaitForUserSignup(t, hostAwait, username)
Expand All @@ -52,6 +54,12 @@ func ensureNoUserSignup(t *testing.T, env, username string) {
err := sandboxui.DeleteUserSignup(t, hostAwait, userSignup)
require.NoError(t, err)
}
case sandboxui.ProdEnv:
userSignup := sandboxui.GetUserSignupThroughKsctl(t, username)
if userSignup != nil {
// delete user signup
sandboxui.DeleteUserSignupThroughKsctl(t, username)
}
}
}

Expand Down Expand Up @@ -125,17 +133,26 @@ func performSignup(t *testing.T, page playwright.Page, env, username string) {
err = tryItButton.Click()
require.NoError(t, err)

if env == sandboxui.TestEnv {
switch env {
case sandboxui.TestEnv:
// add signup to cleanup
awaitilities := testsupport.WaitForDeployments(t)
hostAwait := awaitilities.Host()
userSignup, err := sandboxui.WaitForUserSignup(t, hostAwait, username)
require.NoError(t, err)
cleanup.AddCleanTasks(t, hostAwait.Client, userSignup)
case sandboxui.ProdEnv:
// delete user signup through ksctl
t.Cleanup(func() {
sandboxui.DeleteUserSignupThroughKsctl(t, username)
})
}

// wait for loading icon to disappear
err = loadingIcon.WaitFor(playwright.LocatorWaitForOptions{State: playwright.WaitForSelectorStateHidden})
err = loadingIcon.WaitFor(playwright.LocatorWaitForOptions{
State: playwright.WaitForSelectorStateHidden,
Timeout: playwright.Float(60000), // 1 minute timeout
})
require.NoError(t, err)

// wait for network to be idle (ensures all updates are complete)
Expand Down Expand Up @@ -187,23 +204,45 @@ func verifyDevSandboxAccess(t *testing.T, page playwright.Page, env, testName st
devSandboxPage, err := sandboxui.ClickAndWaitForPopup(t, page, tryItBtn, testName)
require.NoError(t, err)

img := devSandboxPage.GetByRole("img", playwright.PageGetByRoleOptions{
Name: imgName,
})
err = img.WaitFor(playwright.LocatorWaitForOptions{
Timeout: playwright.Float(30000),
})
require.NoError(t, err)
if env == sandboxui.ProdEnv {
// Wait for auth redirect to complete
if strings.Contains(devSandboxPage.URL(), "/oauth/authorize") {
err := devSandboxPage.WaitForURL("**/k8s/cluster/projects/**", playwright.PageWaitForURLOptions{
Timeout: playwright.Float(30000),
})
require.NoError(t, err)
}

h := devSandboxPage.GetByRole("heading", playwright.PageGetByRoleOptions{})
hText, err := h.TextContent()
require.NoError(t, err)
require.Contains(t, hText, logMessage)
// Wait for modal
modal := devSandboxPage.Locator("[data-test='guided-tour-modal']")
err = modal.WaitFor(playwright.LocatorWaitForOptions{
State: playwright.WaitForSelectorStateVisible,
Timeout: playwright.Float(180000), // 3 minutes
})
require.NoError(t, err)

list := devSandboxPage.GetByRole("list", playwright.PageGetByRoleOptions{})
listText, err := list.TextContent()
require.NoError(t, err)
require.Contains(t, listText, "DevSandbox")
modalText, err := modal.TextContent()
require.NoError(t, err)
require.Contains(t, modalText, "Welcome to the new OpenShift experience!")
} else {
img := devSandboxPage.GetByRole("img", playwright.PageGetByRoleOptions{
Name: imgName,
})
err = img.WaitFor(playwright.LocatorWaitForOptions{
Timeout: playwright.Float(30000),
})
require.NoError(t, err)

h := devSandboxPage.GetByRole("heading", playwright.PageGetByRoleOptions{})
hText, err := h.TextContent()
require.NoError(t, err)
require.Contains(t, hText, logMessage)

list := devSandboxPage.GetByRole("list", playwright.PageGetByRoleOptions{})
listText, err := list.TextContent()
require.NoError(t, err)
require.Contains(t, listText, "DevSandbox")
}

require.NoError(t, devSandboxPage.Close())
}
16 changes: 8 additions & 8 deletions testsupport/devsandbox-dashboard/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewLoginPage(page playwright.Page, environment string) *LoginPage {
}

switch environment {
case DevEnv:
case DevEnv, ProdEnv:
lp.LoginUsernameLoc = page.GetByRole("textbox", playwright.PageGetByRoleOptions{
Name: "Red Hat login",
})
Expand Down Expand Up @@ -59,19 +59,19 @@ func NewLoginPage(page playwright.Page, environment string) *LoginPage {
}

func (lp *LoginPage) Navigate(t *testing.T, url string) {
_, err := lp.Page.Goto(url)
maskUsername(t, lp.Page)

_, err := lp.Page.Goto(url, playwright.PageGotoOptions{
Timeout: playwright.Float(60000), // 1 minute timeout
})
require.NoError(t, err)
}

func (lp *LoginPage) Login(t *testing.T, loginUsername, loginPw string) {
// Mask username field
_, err := lp.LoginUsernameLoc.Evaluate(`element => element.style.webkitTextSecurity = 'disc'`, nil)
require.NoError(t, err)

err = lp.LoginUsernameLoc.Fill(loginUsername)
err := lp.LoginUsernameLoc.Fill(loginUsername)
require.NoError(t, err)

if lp.Env == DevEnv {
if lp.Env == DevEnv || lp.Env == ProdEnv {
err := lp.NextBtn.Click()
require.NoError(t, err)
}
Expand Down
2 changes: 2 additions & 0 deletions testsupport/devsandbox-dashboard/popup.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ func ClickAndWaitForPopup(t *testing.T, currentPage playwright.Page, locator pla
var popup playwright.Page
var err error

maskUsername(t, currentPage)

pollErr := wait.PollUntilContextTimeout(context.TODO(), time.Millisecond, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
popup, err = currentPage.ExpectPopup(func() error {
return locator.Click()
Expand Down
13 changes: 9 additions & 4 deletions testsupport/devsandbox-dashboard/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sandboxui

import (
"fmt"
"os"
"path/filepath"
"runtime"
"sync"
Expand All @@ -15,6 +16,7 @@ import (
const (
TestEnv = "ui-e2e-tests"
DevEnv = "dev"
ProdEnv = "prod"
)

var (
Expand Down Expand Up @@ -62,8 +64,11 @@ func Setup(t *testing.T, testName string) playwright.Page {
context, err := browser.NewContext(opts)
require.NoError(t, err)

// save trace
trace(t, context, testName)
// save trace only if not running in CI
// we do not want to expose sensitive information in CI
if os.Getenv("ARTIFACT_DIR") == "" { // not CI environment
trace(t, context, testName)
}

page, err := context.NewPage()
require.NoError(t, err)
Expand All @@ -73,9 +78,9 @@ func Setup(t *testing.T, testName string) playwright.Page {
login := NewLoginPage(page, env)
login.Navigate(t, baseURL)

if env == DevEnv {
if env == DevEnv || env == ProdEnv {
// handle cookie consent
// on dev environment, the cookie consent appears after the login page is loaded
// on dev and prod environments, the cookie consent appears after the login page is loaded
handleCookiesConsent(t, page)
}

Expand Down
55 changes: 44 additions & 11 deletions testsupport/devsandbox-dashboard/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,8 @@ func handleRecordedVideo(t *testing.T, page playwright.Page, targetVideoPath str

// Handle failed test - rename video
if t.Failed() {
// For popup videos, rename with UUID to avoid conflicts when multiple popups exist in the same test
if strings.Contains(targetVideoPath, "popup") {
uuid := filepath.Base(videoPath)
uuid = strings.TrimSuffix(uuid, ".webm")
if len(uuid) > 8 {
uuid = uuid[:8] // Truncate to first 8 chars
}
targetVideoPath = strings.Replace(targetVideoPath, "popup", fmt.Sprintf("popup-%s", uuid), 1)
}

if err := os.Rename(videoPath, targetVideoPath); err != nil {
finalPath := buildPopupVideoPath(targetVideoPath, videoPath)
if err := os.Rename(videoPath, finalPath); err != nil {
t.Logf("failed to rename video %s: %v", videoPath, err)
}
return
Expand All @@ -90,3 +81,45 @@ func handleRecordedVideo(t *testing.T, page playwright.Page, targetVideoPath str
}
})
}

func buildPopupVideoPath(targetPath, videoPath string) string {
if !strings.Contains(targetPath, "popup") {
return targetPath
}
uuid := filepath.Base(videoPath)
uuid = strings.TrimSuffix(uuid, ".webm")
if len(uuid) > 8 {
uuid = uuid[:8]
}
return strings.Replace(targetPath, "popup", fmt.Sprintf("popup-%s", uuid), 1)
}

func maskUsername(t *testing.T, page playwright.Page) {
err := page.Context().AddInitScript(playwright.Script{
Content: playwright.String(`
const applyBlur = () => {
document.querySelectorAll('input[name="username"], span.co-username, span[data-test="username"], [data-test="username"]').forEach(el => {
el.style.filter = 'blur(5px)';
el.style.userSelect = 'none';
});
};

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
applyBlur();
new MutationObserver(applyBlur).observe(document.documentElement, {
childList: true,
subtree: true
});
});
} else {
applyBlur();
new MutationObserver(applyBlur).observe(document.documentElement, {
childList: true,
subtree: true
});
}
`),
})
require.NoError(t, err)
}
Loading
Loading