Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
24 changes: 14 additions & 10 deletions src/services/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,20 @@ private async checkUrlExists(urlToCheck: string, isTenantCheck = false): Promise

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
148 changes: 148 additions & 0 deletions src/test/12.oauthAuthentication.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {
By,
EditorView,
until,
VSBrowser,
WebDriver,
WebView,
Workbench,
NotificationType,
Notification,
ModalDialog
} from "vscode-extension-tester";
import { expect } from "chai";
import { initialize, retryTest, sleep } from "./utils/utils";

const CX_AUTHENTICATION_COMMAND = "ast-results.showAuth";

describe("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 () => {
// Inject a mock token into secrets before running tests using the new command
await bench.executeCommand("ast-results.mockTokenTest");
// Short delay to allow the extension state to update
await new Promise((res) => setTimeout(res, 3000));
await new EditorView().closeAllEditors();
});

// Replace the existing "should open OAuth authentication panel and verify UI elements" test
// with this improved version

it("should open OAuth authentication panel and verify UI elements", retryTest(async function () {
console.log("========== Starting OAuth authentication test ==========");

// Execute the authentication command
await bench.executeCommand(CX_AUTHENTICATION_COMMAND);
console.log("Authentication command executed");
await sleep(5000);

const editorView = new EditorView();
await editorView.openEditor("Checkmarx One Authentication");
console.log("Authentication editor opened");

// Switch to the WebView frame
const webView = new WebView();
await webView.switchToFrame(10000);
console.log("Switched to WebView iframe");

try {
// Enhanced logout process with detailed logging
console.log("Checking if logout button exists...");
const logoutElements = await webView.findWebElements(By.id("logoutButton"));

if (logoutElements.length > 0) {
const isDisplayed = await logoutElements[0].isDisplayed();
console.log(`Logout button found: ${isDisplayed ? "displayed" : "not displayed"}`);

if (isDisplayed) {
console.log("Logging out first...");

// Switch back to main frame before clicking logout
await webView.switchBack();

// Switch back to WebView frame to click the logout button
await webView.switchToFrame(5000);

await logoutElements[0].click();
console.log("Clicked logout button");

// Switch back to main frame to handle the dialog
await webView.switchBack();
await sleep(3000); // Longer wait time

// Try all possible confirmation methods and log detailed information
await handleLogoutConfirmation(driver);

// Switch back to WebView
await webView.switchToFrame(5000);
}
} else {
console.log("Logout button not found - user is likely already logged out");
}

// Rest of the test continues as normal...
console.log("Finding radio buttons for authentication methods...");
const radioButtons = await webView.findWebElements(By.css("input[type='radio']"));
console.log(`Found ${radioButtons.length} radio buttons`);

// Additional test code...
Copy link
Contributor

Choose a reason for hiding this comment

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

where is the test code?
what exactly are we testing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It tests the logout button, meaning it clicks on it and then clicks "yes" in the dialog message that appears.
Additionally, after logging out, it checks that there are two buttons:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added to the test that if the user wasn't logged in, it should first log in and then perform a logout to test this process.

The test also verifies the fields for both OAuth and API Key authentication methods.


console.log("OAuth UI verification completed successfully");
} finally {
try {
await webView.switchBack();
} catch (switchError) {
console.log("Error switching back:", switchError);
}

try {
await new EditorView().closeAllEditors();
} catch (closeError) {
console.log("Error closing editors:", closeError);
}
}
}, 3));

// Optimized function for handling logout confirmation
async function handleLogoutConfirmation(driver) {
try {
// Search directly for notifications - this is the approach that works
const notifications = await driver.findElements(By.className("notification-toast"));
console.log(`Found ${notifications.length} notifications`);

for (const notification of notifications) {
// Check if this is the logout confirmation notification
const notificationText = await notification.getText();

if (notificationText.includes("Are you sure you want to log out?")) {
console.log("Found logout confirmation notification");

// Find the Yes button within this notification
const yesButton = await notification.findElement(By.css(".monaco-button"));
const buttonText = await yesButton.getText();

if (buttonText === "Yes") {
console.log("Clicking 'Yes' button in logout confirmation");
await yesButton.click();
await sleep(2000);
return true;
}
}
}

console.log("Could not find logout confirmation notification");
} catch (error) {
console.log("Error handling logout confirmation:", error);
}

return false;
}
});
13 changes: 8 additions & 5 deletions src/webview/authenticationWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,19 @@ export class AuthenticationWebview {
<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>
Expand All @@ -158,7 +160,8 @@ 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">
</div>
Expand Down
Loading