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
1 change: 1 addition & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export default defineConfig({
viewportWidth: 1920,
viewportHeight: 1080,
defaultCommandTimeout: 60_000,
video: true,
});
114 changes: 88 additions & 26 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,106 @@ declare global {
}

Cypress.Commands.add('login', (provider, username, password) => {
// Check if auth is disabled (for a local development environment).
const idp = provider || KUBEADMIN_IDP;
const user = username || KUBEADMIN_USERNAME;
const pass = password || Cypress.env('KUBEADMIN_PASSWORD');

const selectIdpIfPresent = (idpName: string) => {
cy.get('body').then(($body) => {
if ($body.text().includes(idpName)) {
cy.contains(idpName).should('be.visible').click();
}
});
};

const fillAndSubmitLoginForm = (args: {
idpName: string;
usernameSel: string;
passwordSel: string;
submitSel: string;
user: string;
pass: string;
}) => {
cy.get('main form').should('be.visible');
selectIdpIfPresent(args.idpName);
cy.get(args.usernameSel).type(args.user);
cy.get(args.passwordSel).type(args.pass);
cy.get(args.submitSel).click();
};

const closeTourPopupIfPresent = () => {
cy.get('body').then(($body) => {
if ($body.find(SELECTORS.tourPopup).length) {
cy.get(SELECTORS.tourPopup).click();
}
});
};

cy.visit('/'); // visits baseUrl

cy.visit(''); // visits baseUrl which is set in plugins.js
// If auth is disabled (local dev), skip the login flow entirely.
cy.window().then((win: ConsoleWindowType) => {
if (win.SERVER_FLAGS?.authDisabled) {
cy.log('skipping login, console is running with auth disabled');

cy.contains('li[data-test="nav"]', 'Networking').click();
cy.contains(
'*[data-test-id="nodenetworkconfigurationpolicy-nav-item"]',
'NodeNetworkConfigurationPolicy',
).should('be.visible');
return;
return 'authDisabled' as const;
}

cy.clearCookie('openshift-session-token');

const idp = provider || KUBEADMIN_IDP;

cy.get('main form').should('be.visible');

cy.get('body').then(($body) => {
if ($body.text().includes(idp)) {
cy.contains(idp).should('be.visible').click();
return 'authEnabled' as const;
}).then((authMode) => {
if (authMode === 'authDisabled') return;

// OpenShift redirects unauthenticated users to oauth-openshift.apps... (different origin than
// console-openshift-console...). Cypress 12+ requires cy.origin() for commands on that page.
cy.url().then((currentUrl) => {
const url = new URL(currentUrl);
const isOauthOrigin = url.hostname.includes('oauth-openshift');
Comment on lines +62 to +83
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Cypress 15, after cy.visit('/') redirects to a different origin, is cy.window() allowed before entering cy.origin()? Please cite the official cross-origin testing documentation.

💡 Result:

No, in Cypress 15, after cy.visit('/') redirects to a different origin, cy.window is not allowed before entering cy.origin. Cypress requires that all commands interacting with the application after navigating to a different origin within the same test must be wrapped in cy.origin for the new origin. Attempting cy.window (or similar window-bound commands like cy.document) outside of cy.origin after the cross-origin redirect will result in a cross-origin error or command timeout, as Cypress enforces the same-origin policy strictly. The official cross-origin testing documentation states: "With the exception of cy.origin, Cypress requires that the URLs navigated to have the same origin for the entirety of a single test. If you attempt to visit two different origins, the cy.origin command must be used to wrap Cypress commands of the second visited domain. Otherwise, Cypress commands will timeout after the navigation and will eventually error." Examples include JavaScript redirects (window.location.href), links, or form submissions leading to different origins, all requiring cy.origin for subsequent commands. This behavior was strengthened in v14.0.0 (no default document.domain injection) and continues in v15. While Cypress 15 includes changes to use automation clients for some window-bound commands (e.g., cy.url, cy.title) to subvert cross-origin boundaries in certain cases (PR #31862), cy.window remains bound to the window object and subject to origin checks, requiring cy.origin for cross-origin contexts.

Citations:


Move the cy.window() auth check behind the origin guard.

Line 65 calls cy.window() immediately after cy.visit('/'), but in the auth-enabled path, this redirects to oauth-openshift.apps... (a different origin). Cypress enforces a strict same-origin policy: all commands after a cross-origin redirect must be wrapped in cy.origin(), or they will timeout/error. The current code violates this requirement before ever reaching the cy.origin() block at line 87. Detect the current origin first using cy.url() (which is automation-client-backed in Cypress 15+), and only call cy.window() when still on the console origin.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cypress/support/commands.ts` around lines 62 - 83, The auth check currently
calls cy.window() before determining the page origin, which breaks when
redirected to oauth-openshift; change the flow to call cy.url() first (use
cy.url() / URL parsing to detect oauth-openshift vs console origin), then only
invoke cy.window() to read SERVER_FLAGS?.authDisabled and set authMode when
still on the console origin; otherwise skip to the cy.origin() block for
cross-origin login handling (update references to authMode, cy.window(),
cy.url(), and cy.origin() accordingly).


cy.clearCookie('openshift-session-token');

if (isOauthOrigin) {
Comment on lines +81 to +87
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clear the session before deciding which login path to use.

Lines 81-87 derive isOauthOrigin from the pre-clear URL and only then delete openshift-session-token. If login() starts while a console session is already active, isOauthOrigin stays false and the fallback branch at Lines 115-122 will try to fill a login form that is not on the page. Clear auth state before the initial visit, or revisit after clearing, so the origin check runs against the unauthenticated page.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cypress/support/commands.ts` around lines 81 - 87, The origin check currently
reads isOauthOrigin from the pre-clear URL, causing incorrect branching when a
console session exists; move the clearing of the 'openshift-session-token'
cookie to run before the origin check (i.e., call
cy.clearCookie('openshift-session-token') before obtaining cy.url() /
constructing the URL) or, after clearing, re-run cy.url() (or cy.visit) so
isOauthOrigin is computed against the unauthenticated page; update the logic
inside the login() command that computes isOauthOrigin and the branch that fills
the fallback form (the code referencing isOauthOrigin and the
'openshift-session-token' cookie) so the branch decision always reflects the
post-clear state.

cy.origin(
url.origin,
{
args: {
idpName: idp,
user,
pass,
usernameSel: SELECTORS.usernameInput,
passwordSel: SELECTORS.passwordInput,
submitSel: SELECTORS.submitButton,
},
},
({ idpName, user, pass, usernameSel, passwordSel, submitSel }) => {
cy.get('main form').should('be.visible');

cy.get('body').then(($body) => {
if ($body.text().includes(idpName)) {
cy.contains(idpName).should('be.visible').click();
}
});

cy.get(usernameSel).type(user);
cy.get(passwordSel).type(pass);
cy.get(submitSel).click();
},
);
} else {
fillAndSubmitLoginForm({
idpName: idp,
user,
pass,
usernameSel: SELECTORS.usernameInput,
passwordSel: SELECTORS.passwordInput,
submitSel: SELECTORS.submitButton,
});
}
});

cy.get(SELECTORS.usernameInput).type(username || KUBEADMIN_USERNAME);
cy.get(SELECTORS.passwordInput).type(password || Cypress.env('KUBEADMIN_PASSWORD'));
cy.get(SELECTORS.submitButton).click();

cy.wait(20000);

// Close tour popup if present
cy.get('body').then(($body) => {
if ($body.find(SELECTORS.tourPopup).length) {
cy.get(SELECTORS.tourPopup).click();
}
cy.wait(20000);
closeTourPopupIfPresent();
});
});
});
Expand Down
Loading