Skip to content

Add ui test for Oauth (AST-89331) #1080

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions media/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
});

authButton.addEventListener("click", () => {
messageBox.style.display = "none";

vscode.postMessage({
command: "authenticate",
authMethod: document.querySelector('input[name="authMethod"]:checked')
Expand Down
1 change: 0 additions & 1 deletion src/cx/cx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ export class Cx implements CxPlatform {

async getAstConfiguration() {
const token = await this.context.secrets.get("authCredential");
console.log("Token from secrets:", token);

if (!token) {
return undefined;
Expand Down
1 change: 0 additions & 1 deletion src/cx/cxMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,6 @@ export class CxMock implements CxPlatform {

async getAstConfiguration() {
const token = await this.context.secrets.get("authCredential");
console.log("Token from secrets:", token);

if (!token) {
return undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("ast-results.mockTokenTest", async () => {
const authService = AuthService.getInstance(context);
await authService.saveToken(context, "FAKE_TOKEN_FROM_TEST");
console.log(">> Mock token has been saved to secrets"); // שורת לוג
console.log(">> Mock token has been saved to secrets");
await authService.validateAndUpdateState();
});

Expand Down
29 changes: 15 additions & 14 deletions src/services/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,24 +174,28 @@ private async checkUrlExists(urlToCheck: string, isTenantCheck = false): Promise
authEndpoint: `${baseUri}/auth/realms/${tenant}/protocol/openid-connect/auth`,
tokenEndpoint: `${baseUri}/auth/realms/${tenant}/protocol/openid-connect/token`,
redirectUri: `http://localhost:${port}/checkmarx1/callback`,
scope: 'openid',
scope: 'openid offline_access',
codeVerifier,
codeChallenge,
port
};

try {
const server = await this.startLocalServer(config);
vscode.env.openExternal(vscode.Uri.parse(
`${config.authEndpoint}?` +
`client_id=${config.clientId}&` +
`redirect_uri=${encodeURIComponent(config.redirectUri)}&` +
`response_type=code&` +
`scope=${config.scope}&` +
`code_challenge=${config.codeChallenge}&` +
`code_challenge_method=S256`
));


const authUrl = `${config.authEndpoint}?` +
`client_id=${config.clientId}&` +
`redirect_uri=${encodeURIComponent(config.redirectUri)}&` +
`response_type=code&` +
`scope=${config.scope}&` +
`code_challenge=${config.codeChallenge}&` +
`code_challenge_method=S256`;

const opened = await vscode.env.openExternal(vscode.Uri.parse(authUrl));
if (!opened) {
server.close();
return "";
}
// Now we get both the code and response object
const { code, res } = await this.waitForCode(server);
const token = await this.getRefreshToken(code, config);
Expand Down Expand Up @@ -526,7 +530,6 @@ private async checkUrlExists(urlToCheck: string, isTenantCheck = false): Promise
</head>
<body>
<div class="modal">
<button class="close-button" onclick="window.close()">×</button>
<h1>You're All Set with Checkmarx!</h1>
<div class="icon-container">
<div class="icon">
Expand Down Expand Up @@ -612,13 +615,11 @@ private async checkUrlExists(urlToCheck: string, isTenantCheck = false): Promise
</head>
<body>
<div class="modal">
<button class="close-button" onclick="window.close()">×</button>
<h1>Authentication Failed</h1>
<div class="icon-container">
<span class="error-icon">❌</span>
</div>
<p class="message">${errorMessage}</p>
<button class="close-btn" onclick="window.close()">Close</button>
</div>
</body>
</html>
Expand Down
158 changes: 158 additions & 0 deletions src/test/12.oauthAuthentication.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import {
By,
EditorView,
VSBrowser,
WebDriver,
WebView,
Workbench
} from "vscode-extension-tester";
import { expect } from "chai";
import { retryTest, sleep } from "./utils/utils";

const CX_AUTHENTICATION_COMMAND = "ast-results.showAuth";

describe.skip("Checkmarx OAuth Authentication Tests", () => {
let bench: Workbench;
let driver: WebDriver;

before(async function () {
this.timeout(15000);
bench = new Workbench();
driver = VSBrowser.instance.driver;
});

after(async () => {
await bench.executeCommand("ast-results.mockTokenTest");
await sleep(3000);
await new EditorView().closeAllEditors();
});

it("should open OAuth authentication panel and verify logout/login flow", retryTest(async function () {
await bench.executeCommand(CX_AUTHENTICATION_COMMAND);
await sleep(5000);

const editorView = new EditorView();
await editorView.openEditor("Checkmarx One Authentication");

const webView = new WebView();
await webView.switchToFrame(10000);

let logoutElements = await webView.findWebElements(By.id("logoutButton"));
const isLoggedIn = logoutElements.length > 0 && await logoutElements[0].isDisplayed();

if (!isLoggedIn) {
await webView.switchBack();
await bench.executeCommand("ast-results.mockTokenTest");
await sleep(3000);
await new EditorView().closeAllEditors();
await bench.executeCommand(CX_AUTHENTICATION_COMMAND);
await sleep(3000);
await new EditorView().openEditor("Checkmarx One Authentication");
await webView.switchToFrame(5000);
logoutElements = await webView.findWebElements(By.id("logoutButton"));
}

logoutElements = await webView.findWebElements(By.id("logoutButton"));
if (logoutElements.length > 0 && await logoutElements[0].isDisplayed()) {
await webView.switchBack();
await webView.switchToFrame(5000);
await logoutElements[0].click();
await webView.switchBack();
await sleep(3000);
await handleLogoutConfirmation(driver);
await webView.switchToFrame(5000);
}

const loginForm = await webView.findWebElements(By.id("loginForm"));
expect(loginForm.length).to.be.greaterThan(0, "Login form should be visible when logged out");
}, 3));

it("should verify radio buttons exist", retryTest(async function () {
const webView = new WebView();
await webView.switchToFrame(10000);

const radioButtons = await webView.findWebElements(By.css("input[type='radio']"));
expect(radioButtons.length).to.be.at.least(2, "Should have at least 2 radio buttons (OAuth and API Key)");

await webView.switchBack();
}, 3));

it("should verify OAuth form exists", retryTest(async function () {
const webView = new WebView();
await webView.switchToFrame(10000);

const oauthRadio = await webView.findWebElement(By.css("input[name='authMethod'][value='oauth']"));
await driver.executeScript("arguments[0].click();", oauthRadio);
await sleep(1000);

const oauthForm = await webView.findWebElement(By.id("oauthForm"));
expect(oauthForm).to.not.be.undefined;

await webView.switchBack();
}, 3));

it("should verify OAuth form text labels", retryTest(async function () {
const webView = new WebView();
await webView.switchToFrame(10000);

const oauthForm = await webView.findWebElement(By.id("oauthForm"));
const oauthFormText = await oauthForm.getText();

expect(oauthFormText).to.include("Checkmarx One Base URL:", "Base URL label should be present in OAuth form");
expect(oauthFormText).to.include("Tenant Name:", "Tenant Name label should be present in OAuth form");

await webView.switchBack();
}, 3));

it("should verify OAuth button disabled state", retryTest(async function () {
const webView = new WebView();
await webView.switchToFrame(10000);

const authButton = await webView.findWebElement(By.id("authButton"));
const disabledAttr = await authButton.getAttribute("disabled");
expect(disabledAttr).to.equal("true", "Auth button should be disabled when OAuth fields are empty");

await webView.switchBack();
}, 3));

it("should verify API Key form text labels", retryTest(async function () {
const webView = new WebView();
await webView.switchToFrame(10000);

const apiKeyRadio = await webView.findWebElement(By.css("input[name='authMethod'][value='apiKey']"));
await driver.executeScript("arguments[0].click();", apiKeyRadio);
await sleep(1000);

const apiKeyForm = await webView.findWebElement(By.id("apiKeyForm"));
const apiKeyFormText = await apiKeyForm.getText();

expect(apiKeyFormText).to.include("Checkmarx One API Key:", "API Key label should be present in API Key form");

await webView.switchBack();
}, 3));

it("should verify API Key button disabled state", retryTest(async function () {
const webView = new WebView();
await webView.switchToFrame(10000);

const authButton = await webView.findWebElement(By.id("authButton"));
const disabledAttr = await authButton.getAttribute("disabled");
expect(disabledAttr).to.equal("true", "Auth button should be disabled when API Key field is empty");

await webView.switchBack();
}, 3));

async function handleLogoutConfirmation(driver) {
const notifications = await driver.findElements(By.className("notification-toast"));
for (const notification of notifications) {
const notificationText = await notification.getText();
if (notificationText.includes("Are you sure you want to log out?")) {
const yesButton = await notification.findElement(By.css(".monaco-button"));
await yesButton.click();
await sleep(2000);
return true;
}
}
return false;
}
});
36 changes: 20 additions & 16 deletions src/webview/authenticationWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,41 +115,43 @@ export class AuthenticationWebview {
<html>

<head>
<meta charset="UTF-8">
<link href="${styleBootStrap}" rel="stylesheet">
<link href="${styleAuth}" rel="stylesheet">
<script nonce="${nonce}" src="${scriptBootStrap}"></script>
<title>Checkmarx One Authentication</title>
<meta charset="UTF-8">
<link href="${styleBootStrap}" rel="stylesheet">
<link href="${styleAuth}" rel="stylesheet">
<script nonce="${nonce}" src="${scriptBootStrap}"></script>
<title>Checkmarx One Authentication</title>


</head>

<body>

<div id="loading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Checking authentication...</span>
</div>
</div>
<div class="spinner-border" role="status">
<span class="visually-hidden">Checking authentication...</span>
</div>
</div>
<div id="authContainer" class="auth-container hidden">
<div class="auth-form-title">Checkmarx One Authentication</div>
<div id="loginForm">
<div class="radio-group">

<label style="display: none;">
<!-- <label style="display: none;"> -->
<label>
<input type="radio" name="authMethod" value="oauth" checked> OAuth
</label>

<label>
<input type="radio" name="authMethod" value="apiKey" checked >API Key
<!-- <input type="radio" name="authMethod" value="apiKey" checked >API Key -->
<input type="radio" name="authMethod" value="apiKey">API Key
</label>
</div>

<div style="display: none;" id="oauthForm" class="auth-form">
<div id="oauthForm" class="auth-form">
<!-- <div style="display: none;" id="oauthForm" class="auth-form"> -->
<label for="baseUri" class="form-label">Checkmarx One Base URL:</label>
<input type="text" id="baseUri" class="auth-input" placeholder="Enter Checkmarx One Base URL">
<div id="urls-list" class="autocomplete-items"></div>
<div id="urlError" class="text-danger mt-1" style="display: none;"></div>
<div id="urlError" class="text-danger mt-1" style="display: none;"></div>


<label for="tenant" class="form-label">Tenant Name:</label>
Expand All @@ -158,9 +160,10 @@ export class AuthenticationWebview {
</div>

<!-- (We need to return it to the next div ) (class="hidden">) -->
<div id="apiKeyForm"
<!-- <div id="apiKeyForm" -->
<div id="apiKeyForm" class="hidden">
<label for="apiKey" class="form-label">Checkmarx One API Key:</label>
<input type="password" id="apiKey" placeholder="Enter Checkmarx One API Key" class="auth-input">
<input type="password" id="apiKey" placeholder="Enter Checkmarx One API Key" class="auth-input">
</div>
<button id="authButton" class="auth-button" disabled><img src="${loginIcon}" alt="login"/>Sign in to Checkmarx</button>
</div>
Expand Down Expand Up @@ -292,3 +295,4 @@ export class AuthenticationWebview {
}
}
}

Loading