diff --git a/.github/workflows/run-tests-workflow.yml b/.github/workflows/run-tests-workflow.yml index 3f91d315..624304e0 100644 --- a/.github/workflows/run-tests-workflow.yml +++ b/.github/workflows/run-tests-workflow.yml @@ -322,7 +322,7 @@ jobs: echo "matrix=$(cat gha_matrix.json)" >> "$GITHUB_OUTPUT" e2e-tests: - needs: [generate-e2e-matrix, build-downloadable-vsix] + needs: [generate-e2e-matrix] if: github.event.pull_request.draft == false permissions: write-all runs-on: [self-hosted, vscode-vcast, Linux] diff --git a/CHANGELOG.md b/CHANGELOG.md index edf1035d..41f9cf3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to the "vectorcastTestExplorer" extension will be documented in this file. +## [1.0.31] - 2026-05-07 + +### Added +- Ability to set a VCDB as default and include in the default CFG +- Ability to set VCDB option when creating new Projects / CFG files +- Redesigned new Project / new CFG webviews ## [1.0.30] - 2026-03-24 diff --git a/package.json b/package.json index 32402e3e..34c9dc07 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vectorcasttestexplorer", "displayName": "VectorCAST Test Explorer", "description": "VectorCAST Test Explorer for VS Code", - "version": "1.0.30", + "version": "1.0.31", "license": "MIT", "repository": { "type": "git", @@ -46,7 +46,11 @@ "commands": [ { "command": "vectorcastTestExplorer.createNewProject", - "title": "VectorCAST: Create New Project" + "title": "Create New Project" + }, + { + "command": "vectorcastTestExplorer.createNewCFG", + "title": "Create new CFG" }, { "command": "vectorcastTestExplorer.configure", @@ -104,11 +108,6 @@ "category": "VectorCAST Test Explorer", "title": "Add Testsuite to Compiler" }, - { - "command": "vectorcastTestExplorer.newCompilerInProjectVCAST", - "category": "VectorCAST Test Explorer", - "title": "Create new Compiler in Project" - }, { "command": "vectorcastTestExplorer.addEnviroToProject", "category": "VectorCAST Test Explorer", @@ -169,6 +168,11 @@ "category": "VectorCAST Test Explorer", "title": "Create VectorCAST Environment" }, + { + "command": "vectorcastTestExplorer.defaultVCShell", + "category": "VectorCAST Test Explorer", + "title": "VectorCAST: Set as default Database" + }, { "command": "vectorcastTestExplorer.newEnviroInProjectVCAST", "category": "VectorCAST Test Explorer", @@ -386,6 +390,12 @@ "default": "", "description": "The file path to an existing VectorCAST configuration that should be used when creating new environments. If a value is not specified, the configuration editor will be launched" }, + "vectorcastTestExplorer.databaseLocation": { + "type": "string", + "order": 4, + "default": "", + "description": "The file path to an existing VectorCAST database file that should be used when creating new environments." + }, "vectorcastTestExplorer.showReportOnExecute": { "type": "boolean", "order": 5, @@ -707,7 +717,11 @@ "file/newFile": [ { "command": "vectorcastTestExplorer.createNewProject", - "group": "1_createnew" + "group": "VectorCAST: New Ressources" + }, + { + "command": "vectorcastTestExplorer.createNewCFG", + "group": "VectorCAST: New Ressources" } ], "commandPalette": [ @@ -750,10 +764,6 @@ "command": "vectorcastTestExplorer.addTestsuiteToCompiler", "when": "never" }, - { - "command": "vectorcastTestExplorer.newCompilerInProjectVCAST", - "when": "never" - }, { "command": "vectorcastTestExplorer.addEnviroToProject", "when": "never" @@ -802,6 +812,10 @@ "command": "vectorcastTestExplorer.newEnviroVCAST", "when": "never" }, + { + "command": "vectorcastTestExplorer.defaultVCShell", + "when": "never" + }, { "command": "vectorcastTestExplorer.newEnviroInProjectVCAST", "when": "never" @@ -957,6 +971,11 @@ "when": "resourceLangId == cpp || resourceLangId == c", "group": "2_workspace" }, + { + "command": "vectorcastTestExplorer.defaultVCShell", + "when": "vectorcastTestExplorer.configured && (resourceExtname==.db || resourceFilename==compile_commands.json)", + "group": "2_workspace" + }, { "command": "vectorcastTestExplorer.newEnviroInProjectVCAST", "when": "(resourceLangId == cpp || resourceLangId == c) && vectorcastTestExplorer.globalProjectIsOpenedChecker", @@ -1059,12 +1078,12 @@ "when": "testId =~ /\\.vcm$/" }, { - "command": "vectorcastTestExplorer.addEnviroToProject", + "command": "vectorcastTestExplorer.createNewCFG", "group": "vcast.project", "when": "testId =~ /\\.vcm$/" }, { - "command": "vectorcastTestExplorer.newCompilerInProjectVCAST", + "command": "vectorcastTestExplorer.addEnviroToProject", "group": "vcast.project", "when": "testId =~ /\\.vcm$/" }, diff --git a/src/extension.ts b/src/extension.ts index 7d859e6c..c07476fa 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -79,6 +79,7 @@ import { forceLowerCaseDriveLetter, decodeVar, getFullEnvReport, + normalizePath, } from "./utilities"; import { @@ -102,7 +103,6 @@ import { cleanProjectEnvironment, addEnvToTestsuite, deleteEnvironmentFromProject, - createNewCompilerInProject, createNewProject, } from "./manage/manageSrc/manageCommands"; @@ -149,6 +149,10 @@ import { newTestScript, openCodedTest, ProjectEnvParameters, + createNewCFGFile, + ConfigurationOptions, + updateVCShellDatabase, + updateCFGWithVCShellDatabase, } from "./vcastTestInterface"; import { @@ -1619,6 +1623,51 @@ async function installPreActivationEventHandlers( ); context.subscriptions.push(newEnviroVCASTCommand); + let defaultVCShellCommand = vscode.commands.registerCommand( + "vectorcastTestExplorer.defaultVCShell", + async (fileURI: Uri) => { + await checkPrerequisites(context); + + if (!alreadyConfigured) { + return; + } + + if (fileURI) { + const filePath = fileURI.fsPath; + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + + const defaultConfigurationPath = settings.get( + "configurationLocation", + undefined + ); + + // First update vcshell db setting + await updateVCShellDatabase(filePath); + + // In case we have a default cfg, ask the user if he wants to update the cfg with the current default vcshell db + if (defaultConfigurationPath) { + const choice = await vscode.window.showWarningMessage( + `You have set a default CFG at ${defaultConfigurationPath}. Do you want to include your database in that configuration?`, + { modal: false }, + "Yes", + "No" + ); + + if (choice === "Yes") { + const normalizedCFGPath = normalizePath( + path.dirname(defaultConfigurationPath) + ); + await updateCFGWithVCShellDatabase(filePath, normalizedCFGPath); + } + } + } + } + ); + + context.subscriptions.push(defaultVCShellCommand); + const importEnviroToProject = vscode.commands.registerCommand( "vectorcastTestExplorer.importEnviroToProject", async (_args: vscode.Uri, argList: vscode.Uri[]) => { @@ -2055,32 +2104,69 @@ async function installPreActivationEventHandlers( projectName?: string; compilerName?: string; targetDir?: string; + useDefaultCFG?: boolean; + enableCodedTests?: boolean; + defaultCFG?: boolean; + useDefaultDB?: boolean; }) { - const { projectName, compilerName, targetDir } = message; - // Make sure that Inputs have to be filled and the compiler tag is found + const { + projectName, + compilerName, + targetDir, + useDefaultCFG, + enableCodedTests, + defaultCFG, + useDefaultDB, + } = message; + if (!projectName) { vscode.window.showErrorMessage("Project Name is required."); return; } - if (!compilerName) { - vscode.window.showErrorMessage("Compiler selection is required."); - return; - } - const compilerTag = compilerTagList[compilerName]; - if (!compilerTag) { - vscode.window.showErrorMessage( - `No compiler tag found for "${compilerName}".` + + let compiler: string | undefined; + + if (useDefaultCFG) { + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" ); - return; + const defaultCFGPath = settings.get("configurationLocation"); + + if (!defaultCFGPath) { + vscode.window.showErrorMessage("No default CFG is defined."); + return; + } + compiler = defaultCFGPath; + } else { + if (!compilerName) { + vscode.window.showErrorMessage("Compiler selection is required."); + return; + } + compiler = compilerTagList[compilerName]; + if (!compiler) { + vscode.window.showErrorMessage( + `No compiler tag found for "${compilerName}".` + ); + return; + } } + const configurationOptions: ConfigurationOptions = { + enableCodedTests: !!enableCodedTests, + defaultCFG: !!defaultCFG, + useDefaultDB: !!useDefaultDB, + }; + const base = targetDir ?? workspaceRoot; const projectPath = path.join(base, projectName); - vscode.window.showInformationMessage( - `Creating project "${projectName}" at ${projectPath} using ${compilerName}.` + await createNewProject( + projectPath, + compiler, + !!useDefaultCFG, + configurationOptions ); - await createNewProject(projectPath, compilerTag); + panel.dispose(); } } @@ -2088,6 +2174,7 @@ async function installPreActivationEventHandlers( context.subscriptions.push(createNewProjectCmd); + // Helper function for Webview Content async function getNewProjectWebviewContent( context: vscode.ExtensionContext, panel: vscode.WebviewPanel, @@ -2104,21 +2191,35 @@ async function installPreActivationEventHandlers( const scriptUri = panel.webview.asWebviewUri(scriptOnDisk); const compilersJson = JSON.stringify(Object.keys(compilerTagList)); - // pass default targetDir as workspace root const workspaceJson = JSON.stringify(workspaceRoot); + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + const defaultCFG = settings.get("configurationLocation") ?? ""; + + // Check default DB setting and if file exists + const dbSettingPath = settings.get("databaseLocation"); + let validDBPath = ""; + if (dbSettingPath && fs.existsSync(dbSettingPath)) { + validDBPath = dbSettingPath; + } + let html = fs.readFileSync(htmlPath, "utf8"); const nonce = getNonce(); + html = html.replace( //, ` - - ` + + ` ); html = html.replace("{{ cssUri }}", cssUri.toString()); html = html.replace( @@ -2129,21 +2230,23 @@ async function installPreActivationEventHandlers( return html; } - const newCompilerCmd = vscode.commands.registerCommand( - "vectorcastTestExplorer.newCompilerInProjectVCAST", - async (args: any) => { - // Retrieve project path from the clicked node's 'id' + const createNewCFGCmd = vscode.commands.registerCommand( + "vectorcastTestExplorer.createNewCFG", + async (args?: any) => { + // Project context: command was triggered from a project node const projectPath: string | undefined = args?.id; - if (!projectPath) { - vscode.window.showErrorMessage("No project node provided."); + + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders?.length) { + vscode.window.showErrorMessage("Open a folder first."); return; } + const workspaceRoot = workspaceFolders[0].uri.fsPath; - // Create webview panel const baseDir = resolveWebviewBase(context); const panel = vscode.window.createWebviewPanel( - "newCompiler", - "Create Compiler in Project", + "newCFG", + projectPath ? "Create Compiler in Project" : "Create New CFG File", vscode.ViewColumn.Active, { enableScripts: true, @@ -2152,104 +2255,154 @@ async function installPreActivationEventHandlers( } ); - // Load HTML into webview - panel.webview.html = await getNewCompilerWebviewContent( + panel.webview.html = await getNewCFGWebviewContent( context, panel, - projectPath + compilerTagList, + workspaceRoot ); - // Dispatch table - const dispatch: Record Promise | void> = { - submit: handleSubmit, - cancel: () => panel.dispose(), - }; - panel.webview.onDidReceiveMessage( - (message) => dispatch[message.command]?.(message), - undefined, - context.subscriptions - ); + async (msg) => { + switch (msg.command) { + case "browseForDir": { + const selected = await vscode.window.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + openLabel: "Select Output Folder", + defaultUri: vscode.Uri.file(workspaceRoot), + }); + if (selected?.length) { + panel.webview.postMessage({ + command: "setTargetDir", + targetDir: selected[0].fsPath, + }); + } + break; + } + case "submit": { + const compilerName: string | undefined = msg.compilerName?.trim(); + const targetDir: string = msg.targetDir || workspaceRoot; + const enableCodedTests: boolean = !!msg.enableCodedTests; + const defaultCFG: boolean = !!msg.defaultCFG; + const useDefaultDB: boolean = !!msg.useDefaultDB; + + if (!compilerName) { + vscode.window.showErrorMessage( + "Compiler selection is required." + ); + return; + } - // Handle submission - async function handleSubmit(message: { compilerName?: string }) { - const compilerName = message.compilerName; - if (!compilerName) { - vscode.window.showErrorMessage("Compiler Name is required."); - return; - } + const compilerTag = compilerTagList[compilerName]; + if (!compilerTag) { + vscode.window.showErrorMessage( + `No compiler tag found for "${compilerName}".` + ); + return; + } - const compilerTemplate = compilerTagList[compilerName]; - if (!compilerTemplate) { - vscode.window.showErrorMessage( - `Compiler Template Name was not found for ${compilerName}.` - ); - return; - } + const configurationOptions: ConfigurationOptions = { + enableCodedTests, + defaultCFG, + useDefaultDB, + }; + + // Always create the CFG file in the user-chosen folder + const createdCFGPath = await createNewCFGFile( + targetDir, + compilerTag, + configurationOptions + ); - if (projectPath) { - vscode.window.showInformationMessage( - `Adding compiler ${compilerName} to project ${projectPath}` - ); - await createNewCompilerInProject(projectPath, compilerTemplate); - } else { - vscode.window.showErrorMessage( - "Project Path is not defined. Cannot add compiler." - ); - return; - } - panel.dispose(); - } + // If triggered from a project node, also add the compiler to the project + if (projectPath) { + if (createdCFGPath) { + await addCompilerToProject(projectPath, createdCFGPath); + } else { + vscode.window.showErrorMessage( + "CFG creation failed — compiler was not added to the project." + ); + } + } + + panel.dispose(); + break; + } + case "cancel": { + panel.dispose(); + break; + } + } + }, + undefined, + context.subscriptions + ); } ); - context.subscriptions.push(newCompilerCmd); + context.subscriptions.push(createNewCFGCmd); - async function getNewCompilerWebviewContent( + async function getNewCFGWebviewContent( context: vscode.ExtensionContext, panel: vscode.WebviewPanel, - projectPath: string + compilerTagList: Record, + workspaceRoot: string ): Promise { + // Build paths for webview files const base = resolveWebviewBase(context); - const cssOnDisk = vscode.Uri.file( - path.join(base, "css", "newCompiler.css") - ); + const cssOnDisk = vscode.Uri.file(path.join(base, "css", "newCFG.css")); const scriptOnDisk = vscode.Uri.file( - path.join(base, "webviewScripts", "newCompiler.js") + path.join(base, "webviewScripts", "newCFG.js") ); - const htmlPath = path.join(base, "html", "newCompiler.html"); + const htmlPath = path.join(base, "html", "newCFG.html"); + // Convert to URIs const cssUri = panel.webview.asWebviewUri(cssOnDisk); const scriptUri = panel.webview.asWebviewUri(scriptOnDisk); - // For demo, hard‑coded compiler list - const compilerList = JSON.stringify(Object.keys(compilerTagList)); - const projectDir = path.resolve(projectPath); - const projectName = JSON.stringify(path.basename(projectDir)); + // JSON list of compilers for autocomplete + const compilerList = JSON.stringify(Object.keys(compilerTagList ?? {})); + + // Check DB setting + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + const dbSettingPath = settings.get("databaseLocation"); + let validDBPath = ""; + if (dbSettingPath && fs.existsSync(dbSettingPath)) { + validDBPath = dbSettingPath; + } let html = fs.readFileSync(htmlPath, "utf8"); const nonce = getNonce(); + + // Build CSP meta const csp = ` - - `; - html = html.replace(//, `${csp}`); + + `; + + // Insert CSP immediately after the opening + html = html.replace(//i, `${csp}`); + html = html.replace(/{{\s*cssUri\s*}}/g, cssUri.toString()); + html = html.replace( + /<\/script>/i, + `` + ); - html = html - .replace(/{{\s*cssUri\s*}}/g, cssUri.toString()) - .replace( - /` - ) - .replace( - /<\/head>/, - `\n` - ); + // Inline script to expose data to the webview client + const injectedScript = ``; + html = html.replace(/\{\{\s*compilerDataScript\s*\}\}/g, injectedScript); return html; } diff --git a/src/manage/manageSrc/manageCommands.ts b/src/manage/manageSrc/manageCommands.ts index ae7d82d9..55fe47ae 100644 --- a/src/manage/manageSrc/manageCommands.ts +++ b/src/manage/manageSrc/manageCommands.ts @@ -23,7 +23,10 @@ import { getEnviroNodeData } from "../../testData"; import { executeWithRealTimeEchoWithProgress } from "../../vcastCommandRunner"; -import { manageCommandToUse } from "../../vcastInstallation"; +import { + getVectorCastInstallationLocation, + manageCommandToUse, +} from "../../vcastInstallation"; import { checkIfEnvironmentIsBuildMultipleTimes, @@ -44,6 +47,10 @@ import { } from "../../testPane"; import { normalizePath } from "../../utilities"; import { viewResultsReportVC } from "../../reporting"; +import { + ConfigurationOptions, + updateCFGWithVCShellDatabase, +} from "../../vcastTestInterface"; const path = require("path"); @@ -79,54 +86,23 @@ export async function buildProjectEnvironment( ); } -/** - * Creates a new compiler in a selected project - * @param projectPath Path to project - * @param compiler Selected Compiler - * @returns - */ -export async function createNewCompilerInProject( - projectPath: string, - compiler: string -) { - const projectName = path.basename(projectPath); - const projectLocation = projectPath.split(".vcm")[0]; - // We save all new created compilers in a compilers dir - // Check if it already exists, otherwise create it - if (!fs.existsSync(projectLocation)) { - vectorMessage(`${projectLocation} does not exist.`); - return; - } - const projectCompilerPath = path.join(projectLocation, "compilers"); - if (!fs.existsSync(projectCompilerPath)) { - await vscode.workspace.fs.createDirectory( - vscode.Uri.file(projectCompilerPath) - ); - } - - const compilerPath = await createNewCFGFromCompiler( - compiler, - projectCompilerPath - ); - - if (compilerPath) { - const compilerName = path.basename(compilerPath); - await addCompilerToProject(projectPath, compilerPath); - vectorMessage(`Added Compiler ${compilerName} to Project ${projectName}`); - } else { - vectorMessage(`No Compiler found for Project ${projectName}`); - } -} - /** * Creates a new Project including a new Compiler * @param projectPath Path to the new project file - * @param compiler Compiler Name + * @param compiler Compiler Tag OR compiler path in case we are using the default CFG from the settings + * @param usingDefaultCFG Boolean, if the user selected to use the default cfg in the settings */ -export async function createNewProject(projectPath: string, compiler: string) { +export async function createNewProject( + projectPath: string, + compiler: string, + usingDefaultCFG: boolean = false, + configurationOptions?: ConfigurationOptions +) { const projectName = path.basename(projectPath); const projectLocation = path.dirname(projectPath); - const progressMessage = `Creating new Project ${projectName} ...`; + + // Create the Project Directory Structure + const progressMessage = `Creating new Project ${projectName} ...`; const manageArgs = [`-p${projectName}`, `--create`, "--force"]; await executeWithRealTimeEchoWithProgress( @@ -136,7 +112,105 @@ export async function createNewProject(projectPath: string, compiler: string) { progressMessage ); - await createNewCompilerInProject(projectPath, compiler); + // Configure the Compiler / CFG + if (usingDefaultCFG) { + // Scenario A: Use an existing CFG file + if (configurationOptions?.useDefaultDB) { + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + const dbPath = settings.get("databaseLocation"); + + if (dbPath && fs.existsSync(dbPath)) { + // updateCFGWithVCShellDatabase expects the DIRECTORY containing the CFG + await updateCFGWithVCShellDatabase(dbPath, path.dirname(compiler)); + } else { + vscode.window.showWarningMessage( + "Could not set Default Database: the database file defined in settings could not be found." + ); + } + } + + await addCompilerToProject(projectPath, compiler); + } else { + // Scenario B: Create a NEW CFG based on a Compiler Tag + // 'compiler' here is the Compiler Tag (e.g., "GNU_Native") + + // Create the CFG file inside the new project directory + const createdCfgPath = await createNewCFGFromCompiler( + compiler, + projectPath + ); + + // Apply Configuration Options to the freshly created CFG. + if (configurationOptions && createdCfgPath) { + const vcDir = getVectorCastInstallationLocation(); + if (vcDir) { + const clicastCmd = path.join(vcDir, "clicast"); + + // Coded Tests option + const codedFlag = configurationOptions.enableCodedTests + ? "TRUE" + : "FALSE"; + const codedOptionArgs = [ + "-lc", + "option", + "VCAST_CODED_TESTS_SUPPORT", + codedFlag, + ]; + + await executeWithRealTimeEchoWithProgress( + clicastCmd, + codedOptionArgs, + projectPath, + "Setting VCAST_CODED_TESTS_SUPPORT in Project CFG" + ); + + // Default Database option + if (configurationOptions.useDefaultDB) { + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + const dbPath = settings.get("databaseLocation"); + + if (dbPath && fs.existsSync(dbPath)) { + // Pass the directory of the CFG, matching defaultVCShell usage + await updateCFGWithVCShellDatabase( + dbPath, + path.dirname(createdCfgPath) + ); + } else { + vscode.window.showWarningMessage( + "Could not set Default Database: the database file defined in settings could not be found." + ); + } + } + + // Set this newly created CFG as the workspace default if requested + if (configurationOptions.defaultCFG) { + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + await settings.update( + "configurationLocation", + createdCfgPath, + vscode.ConfigurationTarget.Workspace + ); + } + } else { + vscode.window.showErrorMessage( + "Could not determine VectorCAST location. Configuration options could not be applied." + ); + } + } + + // Now that the CFG has all options applied, import it into the project. + // If createdCfgPath is undefined the user already got a message from + // createNewCFGFromCompiler, so no extra handling needed here. + if (createdCfgPath) { + await addCompilerToProject(projectPath, createdCfgPath); + } + } } export async function cleanProjectEnvironment( @@ -371,6 +445,7 @@ export async function addCompilerToProject( ); await refreshAllExtensionData(); + vectorMessage(`${pathToCFG} has been added into project ${projectName}`); } /** diff --git a/src/manage/webviews/css/newCFG.css b/src/manage/webviews/css/newCFG.css new file mode 100644 index 00000000..452d744c --- /dev/null +++ b/src/manage/webviews/css/newCFG.css @@ -0,0 +1,254 @@ +/* Reset and base */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #1e1e1e; + color: #d4d4d4; + display: flex; + align-items: center; + justify-content: center; + height: 100vh; +} + +/* Modal container */ +.modal { + width: 700px; + background-color: #252526; + padding: 25px; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0,0,0,0.3); + display: flex; + flex-direction: column; +} + +/* Modal title */ +.modal h2 { + text-align: center; + color: #ffffff; + margin-bottom: 15px; + font-size: 22px; +} + +/* Labels and inputs */ +label, .toggle-label { + font-size: 16px; + color: #d4d4d4; +} + +input[type="text"], select { + padding: 10px; + font-size: 14px; + background-color: #3c3c3c; + color: #d4d4d4; + border: 1px solid #555; + border-radius: 4px; + width: 100%; +} + +/* Folder-picker row */ +.single-input-container { + display: grid; + grid-template-columns: 1fr auto; + gap: 10px; + align-items: center; + margin-bottom: 15px; +} + +/* “Select” button beside the input */ +.select-button { + background-color: #007acc; + color: white; + border: none; + border-radius: 4px; + padding: 10px 16px; + font-size: 14px; + cursor: pointer; + transition: background-color 0.3s; +} + +.select-button:hover { + background-color: #005f99; +} + +/* Add spacing between label and autocomplete */ +label[for="compilerInput"] { + margin-bottom: 10px; +} + +/* Autocomplete */ +.autocomplete { + position: relative; + width: 100%; +} + +.suggestions { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: #2a2a2a; + border: 1px solid #555; + max-height: 180px; + overflow-y: auto; + list-style: none; + padding: 0; + margin-top: 4px; + display: none; + z-index: 10; + text-align: left; +} + +.suggestions.visible { + display: block; +} + +.suggestions li { + padding: 8px 12px; + cursor: pointer; + font-size: 14px; +} + +.suggestions li:hover, +.suggestions li.active { + background: #007acc; + color: white; +} + +/* =========================== + Options section + =========================== */ +.options-section { + margin-top: 20px; + padding-top: 10px; + border-top: 1px solid #444; + width: 80%; + margin-left: auto; + margin-right: auto; +} + +.options-section h3 { + font-size: 16px; + font-weight: 600; + margin-bottom: 10px; + color: #ffffff; + text-align: center; +} + +.option-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 0; + width: 100%; +} + +.option-row .toggle-label { + font-size: 16px; + color: #d4d4d4; +} + +/* =========================== + VS Code–style toggle + =========================== */ +.vscode-toggle { + display: inline-block; + cursor: pointer; +} + +.toggle-switch { + display: inline-block; + position: relative; + width: 44px; + height: 24px; +} + +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; + margin: 0; +} + +.toggle-switch .slider { + position: absolute; + inset: 0; + background: #3c3c3c; + border: 1px solid #555; + border-radius: 12px; + transition: background 0.12s ease, border-color 0.12s ease; + box-sizing: border-box; +} + +.toggle-switch .slider::before { + content: ""; + position: absolute; + top: 50%; + left: 1px; + width: 22px; + height: 22px; + border-radius: 50%; + background: #d4d4d4; + box-shadow: 0 1px 0 rgba(0,0,0,0.25); + transform: translateY(-50%); + transition: left 0.12s ease, background 0.12s ease; +} + +.toggle-switch input:checked + .slider { + background: #007acc; + border-color: #007acc; +} + +.toggle-switch input:checked + .slider::before { + left: 21px; + background: #ffffff; +} + +.toggle-switch input:focus + .slider { + box-shadow: 0 0 0 3px rgba(0,122,204,0.14); +} + +.vscode-toggle:hover .slider { + filter: brightness(1.06); +} + +/* =========================== + Buttons + =========================== */ +.button-container { + display: flex; + justify-content: space-between; /* cancel left, create right */ + margin-top: 25px +} + +.primary-button, +.cancel-button { + padding: 10px 20px; + font-size: 16px; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s; +} + +.primary-button { + background-color: #007acc; + color: white; +} + +.primary-button:hover { + background-color: #005f99; +} + +.cancel-button { + background-color: #cc4444; + color: white; +} + +.cancel-button:hover { + background-color: #992222; +} diff --git a/src/manage/webviews/css/newProject.css b/src/manage/webviews/css/newProject.css index 5be0367a..e386e4a8 100644 --- a/src/manage/webviews/css/newProject.css +++ b/src/manage/webviews/css/newProject.css @@ -22,36 +22,46 @@ body { padding: 25px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.3); + display: flex; + flex-direction: column; + max-height: 95vh; + overflow-y: auto; } -/* Center only the title, not all modal text */ +/* Modal Main Title */ .modal h2 { text-align: center; -} - -h2 { color: #ffffff; - margin-bottom: 15px; + margin-bottom: 20px; font-size: 22px; + font-weight: 600; } -/* Labels and inputs */ -label { - font-size: 16px; +/* Generic Labels */ +label, .toggle-label { + font-size: 14px; + color: #cccccc; display: block; - text-align: left; - margin-top: 12px; margin-bottom: 6px; + font-weight: 400; } +/* Inputs */ input[type="text"], select { - padding: 10px; + padding: 8px 10px; font-size: 14px; background-color: #3c3c3c; color: #d4d4d4; - border: 1px solid #555; + border: 1px solid #3c3c3c; border-radius: 4px; width: 100%; + margin-bottom: 15px; + transition: border-color 0.2s; +} + +input[type="text"]:focus, select:focus { + border-color: #007acc; + outline: none; } /* Folder-picker row */ @@ -59,98 +69,246 @@ input[type="text"], select { display: grid; grid-template-columns: 1fr auto; gap: 10px; - align-items: center; + align-items: start; margin-bottom: 15px; } +.single-input-container input[type="text"] { + margin-bottom: 0; +} -/* “Select” button beside the input */ +/* Select Button */ .select-button { background-color: #007acc; color: white; border: none; border-radius: 4px; - padding: 10px 16px; + padding: 0 16px; font-size: 14px; cursor: pointer; + height: 35px; /* Match input height */ transition: background-color 0.3s; + display: flex; + align-items: center; } - -.select-button:hover { - background-color: #005f99; -} +.select-button:hover { background-color: #005f99; } /* Autocomplete */ .autocomplete { position: relative; width: 100%; } - .suggestions { position: absolute; top: 100%; left: 0; right: 0; - background: #2a2a2a; - border: 1px solid #555; + background: #252526; + border: 1px solid #454545; max-height: 180px; overflow-y: auto; list-style: none; padding: 0; - margin-top: 4px; + margin-top: 2px; display: none; - z-index: 10; - text-align: left; /* ensure left alignment */ -} - -.suggestions.visible { - display: block; + z-index: 100; + box-shadow: 0 4px 6px rgba(0,0,0,0.3); } - +.suggestions.visible { display: block; } .suggestions li { padding: 8px 12px; cursor: pointer; font-size: 14px; + border-bottom: 1px solid #333; } - +.suggestions li:last-child { border-bottom: none; } .suggestions li:hover, .suggestions li.active { background: #007acc; color: white; } -/* Button container */ +/* ========================================= + Options Section & Headers + ========================================= */ + +.options-section { + margin-top: 15px; + padding-top: 15px; + border-top: 1px solid #3e3e3e; + width: 98%; + margin-left: auto; + margin-right: auto; +} + +/* MAIN SECTION TITLE (Compiler Selection) */ +.options-section h3 { + font-size: 16px; + font-weight: 700; + color: #ffffff; + margin: 0 0 15px 0; + text-align: center; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Selection Boxes */ +.selection-box { + border: 1px solid #444; + border-radius: 6px; + padding: 15px; + margin-bottom: 15px; + background-color: #2d2d2d; +} + +/* Default Compiler Row */ +#defaultCompilerRow { + display: flex; + align-items: center; + justify-content: space-between; +} +#defaultCompilerRow .toggle-label { + margin: 0; + color: #fff; + font-weight: 600; +} +#defaultCFGPath { + display: block; + margin-top: 4px; + font-size: 12px; + color: #888; + font-weight: 400; + word-break: break-all; +} + +/* OR Separator */ +#orSeparator { + text-align: center; + margin: 10px 0; +} +#orSeparator h3 { + font-size: 14px; + color: #ffffff; + font-weight: 700; + margin: 0; + text-transform: none; +} + +/* New Compiler Section */ +#newCompilerSection { + display: block; +} + +/* SUB-SECTION TITLE (Configuration Options) */ +.config-options-wrapper { + margin-top: 20px; + padding-top: 15px; + border-top: 1px dashed #444; +} + +.config-options-wrapper h4 { + font-size: 14px; + color: #ffffff; + font-weight: 700; + margin-bottom: 12px; + display: block; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Option Rows */ +.option-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 0; + width: 100%; +} + +.option-row .toggle-label { + font-size: 14px; + color: #d4d4d4; + font-weight: 400; + margin-bottom: 0; +} + +/* Toggle Switch */ +.vscode-toggle { + display: inline-block; + cursor: pointer; +} +.toggle-switch { + display: inline-block; + position: relative; + width: 40px; + height: 20px; + vertical-align: middle; +} +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} +.toggle-switch .slider { + position: absolute; + inset: 0; + background: #3c3c3c; + border: 1px solid #555; + border-radius: 10px; + transition: .2s; +} +.toggle-switch .slider::before { + content: ""; + position: absolute; + height: 16px; + width: 16px; + left: 2px; + bottom: 1px; + background-color: #ccc; + border-radius: 50%; + transition: .2s; +} +.toggle-switch input:checked + .slider { + background-color: #007acc; + border-color: #007acc; +} +.toggle-switch input:checked + .slider::before { + transform: translateX(20px); + background-color: white; +} +.vscode-toggle:hover .slider { + border-color: #666; +} + +/* Buttons */ .button-container { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; + padding-top: 10px; } -/* Primary & cancel buttons */ .primary-button, .cancel-button { - padding: 10px 20px; - font-size: 16px; + padding: 8px 24px; + font-size: 14px; + font-weight: 600; border: none; border-radius: 4px; cursor: pointer; - transition: background-color 0.3s; + transition: background-color 0.2s; } .primary-button { background-color: #007acc; color: white; } - -.primary-button:hover { - background-color: #005f99; -} +.primary-button:hover { background-color: #005f99; } .cancel-button { background-color: #cc4444; color: white; } - .cancel-button:hover { background-color: #992222; -} +} \ No newline at end of file diff --git a/src/manage/webviews/html/newCFG.html b/src/manage/webviews/html/newCFG.html new file mode 100644 index 00000000..92dcf640 --- /dev/null +++ b/src/manage/webviews/html/newCFG.html @@ -0,0 +1,73 @@ + + + + + + Create New CFG File + + + + + + + {{ compilerDataScript }} + + + + \ No newline at end of file diff --git a/src/manage/webviews/html/newProject.html b/src/manage/webviews/html/newProject.html index d5937b84..60097c1b 100644 --- a/src/manage/webviews/html/newProject.html +++ b/src/manage/webviews/html/newProject.html @@ -25,10 +25,86 @@

Create New Project

- -
- -
    +
    +

    Compiler Selection

    + + + + + + + + +
    + + +
    + +
    + +
      +
      +
      + + +
      +

      Configuration Options:

      + +
      + + +
      + +
      + + +
      + + + + +
      +
      diff --git a/src/manage/webviews/webviewScripts/newCFG.js b/src/manage/webviews/webviewScripts/newCFG.js new file mode 100644 index 00000000..e4290d5e --- /dev/null +++ b/src/manage/webviews/webviewScripts/newCFG.js @@ -0,0 +1,121 @@ +const vscode = acquireVsCodeApi(); + +globalThis.addEventListener("DOMContentLoaded", () => { + const compilers = globalThis.compilerData || []; + const defaultDB = globalThis.defaultDB || ""; + + // Folder picker + const targetInput = document.getElementById("targetDirInput"); + let targetDir = globalThis.defaultDir || ""; + targetInput.value = targetDir; + + document.getElementById("btnBrowse").addEventListener("click", () => { + vscode.postMessage({ command: "browseForDir" }); + }); + + globalThis.addEventListener("message", (e) => { + if (e.data.command === "setTargetDir") { + targetDir = e.data.targetDir; + targetInput.value = targetDir; + } + }); + + // Compiler autocomplete + const compInput = document.getElementById("compilerInput"); + const suggestions = document.getElementById("suggestions"); + + // Toggle checkboxes + const codedCheckbox = document.getElementById("enableCodedTests"); + const defaultCheckbox = document.getElementById("defaultCFG"); + + // DB elements + const dbOptionRow = document.getElementById("dbOptionRow"); + const dbPathLabel = document.getElementById("dbPathLabel"); + const dbCheckbox = document.getElementById("useDefaultDB"); + + if (globalThis.enableCodedTests !== undefined && codedCheckbox) { + codedCheckbox.checked = !!globalThis.enableCodedTests; + } + if (globalThis.defaultCFG !== undefined && defaultCheckbox) { + defaultCheckbox.checked = !!globalThis.defaultCFG; + } + + if (defaultDB) { + dbOptionRow.style.display = "flex"; + dbPathLabel.textContent = defaultDB; + } else { + dbOptionRow.style.display = "none"; + } + + // Autocomplete + let filtered = [], activeIndex = -1; + + function renderSuggestions() { + suggestions.innerHTML = ""; + if (!filtered.length) return suggestions.classList.remove("visible"); + + filtered.forEach((item, i) => { + const li = document.createElement("li"); + li.textContent = item; + if (i === activeIndex) li.classList.add("active"); + li.addEventListener("mousedown", () => { + compInput.value = item; + suggestions.classList.remove("visible"); + }); + suggestions.appendChild(li); + }); + suggestions.classList.add("visible"); + } + + function updateSuggestions(showAll = false) { + const q = (compInput.value || "").toLowerCase().trim(); + filtered = showAll || !q + ? [...compilers] + : compilers.filter(c => c.toLowerCase().includes(q)); + activeIndex = -1; + renderSuggestions(); + } + + compInput.addEventListener("input", () => updateSuggestions()); + compInput.addEventListener("focus", () => updateSuggestions(true)); + + compInput.addEventListener("keydown", e => { + if (!filtered.length) return; + if (e.key === "ArrowDown") { + e.preventDefault(); + activeIndex = (activeIndex + 1) % filtered.length; + renderSuggestions(); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + activeIndex = (activeIndex - 1 + filtered.length) % filtered.length; + renderSuggestions(); + } else if (e.key === "Enter") { + e.preventDefault(); + if (filtered[activeIndex]) { + compInput.value = filtered[activeIndex]; + suggestions.classList.remove("visible"); + } + } else if (e.key === "Escape") { + suggestions.classList.remove("visible"); + } + }); + + document.addEventListener("click", e => { + if (!e.target.closest(".autocomplete")) suggestions.classList.remove("visible"); + }); + + document.getElementById("btnSubmit").addEventListener("click", () => { + vscode.postMessage({ + command: "submit", + targetDir, + compilerName: (compInput.value || "").trim(), + enableCodedTests: !!(codedCheckbox && codedCheckbox.checked), + defaultCFG: !!(defaultCheckbox && defaultCheckbox.checked), + useDefaultDB: !!(dbCheckbox && dbCheckbox.checked), + }); + }); + + document.getElementById("btnCancel").addEventListener("click", () => { + vscode.postMessage({ command: "cancel" }); + }); +}); \ No newline at end of file diff --git a/src/manage/webviews/webviewScripts/newProject.js b/src/manage/webviews/webviewScripts/newProject.js index ce7abfeb..c0ac067b 100644 --- a/src/manage/webviews/webviewScripts/newProject.js +++ b/src/manage/webviews/webviewScripts/newProject.js @@ -1,26 +1,76 @@ const vscode = acquireVsCodeApi(); -window.addEventListener("DOMContentLoaded", () => { - const compilers = window.compilerData || []; - const defaultDir = window.defaultDir || ""; +globalThis.addEventListener("DOMContentLoaded", () => { + const compilers = globalThis.compilerData || []; + const defaultCFG = globalThis.defaultCFG || ""; + const defaultDB = globalThis.defaultDB || ""; + + // DOM Elements + const defaultRow = document.getElementById("defaultCompilerRow"); + const defaultPath = document.getElementById("defaultCFGPath"); + const orSeparator = document.getElementById("orSeparator"); + const useDefaultCheckbox = document.getElementById("useDefaultCompiler"); + const newCompilerSection = document.getElementById("newCompilerSection"); const targetInput = document.getElementById("targetDirInput"); - const browseBtn = document.getElementById("btnBrowse"); - const nameInput = document.getElementById("projectNameInput"); - const compInput = document.getElementById("compilerInput"); + const browseBtn = document.getElementById("btnBrowse"); + const nameInput = document.getElementById("projectNameInput"); + const compInput = document.getElementById("compilerInput"); const suggestions = document.getElementById("suggestions"); - // initialize target folder - let targetDir = defaultDir; - targetInput.value = targetDir; + // CFG Option Checkboxes + const codedCheckbox = document.getElementById("enableCodedTests"); + const defaultCFGCheckbox = document.getElementById("defaultCFG"); + + // DB Option Elements + const dbOptionRow = document.getElementById("dbOptionRow"); + const dbPathLabel = document.getElementById("dbPathLabel"); + const dbCheckbox = document.getElementById("useDefaultDB"); + + // --- Initialization Logic --- + + // Show default CFG row + OR only if defaultCFG exists + if (defaultCFG) { + defaultRow.style.display = "flex"; + orSeparator.style.display = "block"; + defaultPath.textContent = defaultCFG; + } else { + defaultRow.style.display = "none"; + orSeparator.style.display = "none"; + } + + // Show Default DB row if a valid path exists + if (defaultDB) { + dbOptionRow.style.display = "flex"; + dbPathLabel.textContent = defaultDB; + } else { + dbOptionRow.style.display = "none"; + } + + function updateCompilerVisibility() { + if (useDefaultCheckbox && useDefaultCheckbox.checked) { + newCompilerSection.style.display = "none"; + if (defaultCFG) orSeparator.style.display = "none"; + } else { + newCompilerSection.style.display = "block"; + if (defaultCFG) orSeparator.style.display = "block"; + } + } + + if (useDefaultCheckbox) { + useDefaultCheckbox.addEventListener("change", updateCompilerVisibility); + } + updateCompilerVisibility(); - // browse for folder + // --- Target Directory Logic --- + let targetDir = globalThis.defaultDir || ""; + targetInput.value = targetDir; + browseBtn.addEventListener("click", () => { vscode.postMessage({ command: "browseForDir" }); }); - - // receive chosen folder - window.addEventListener("message", (e) => { + + globalThis.addEventListener("message", (e) => { const msg = e.data; if (msg.command === "setTargetDir") { targetDir = msg.targetDir; @@ -28,11 +78,13 @@ window.addEventListener("DOMContentLoaded", () => { } }); - // autocomplete setup (unchanged)... + // --- Autocomplete Logic --- let filtered = [], activeIndex = -1; + function renderSuggestions() { suggestions.innerHTML = ""; if (!filtered.length) return suggestions.classList.remove("visible"); + filtered.forEach((item, i) => { const li = document.createElement("li"); li.textContent = item; @@ -45,16 +97,20 @@ window.addEventListener("DOMContentLoaded", () => { }); suggestions.classList.add("visible"); } + function updateSuggestions(showAll = false) { - const q = compInput.value.toLowerCase().trim(); + const q = (compInput.value || "").toLowerCase().trim(); filtered = showAll || !q ? [...compilers] : compilers.filter(c => c.toLowerCase().includes(q)); activeIndex = -1; renderSuggestions(); } + compInput.addEventListener("input", () => updateSuggestions()); compInput.addEventListener("focus", () => updateSuggestions(true)); + compInput.addEventListener("click", () => updateSuggestions(true)); // Ensure click triggers it + compInput.addEventListener("keydown", e => { if (!filtered.length) return; if (e.key === "ArrowDown") { @@ -75,21 +131,30 @@ window.addEventListener("DOMContentLoaded", () => { suggestions.classList.remove("visible"); } }); + document.addEventListener("click", e => { if (!e.target.closest(".autocomplete")) suggestions.classList.remove("visible"); }); - // submit + // --- Submit Logic --- document.getElementById("btnSubmit").addEventListener("click", () => { + const isUsingDefault = useDefaultCheckbox && useDefaultCheckbox.checked; + vscode.postMessage({ command: "submit", projectName: nameInput.value.trim(), - compilerName: compInput.value.trim(), - targetDir + targetDir, + useDefaultCFG: isUsingDefault, + // Send compiler name only if NOT using default CFG + compilerName: isUsingDefault ? undefined : compInput.value.trim(), + // Send compiler options + enableCodedTests: !!(codedCheckbox && codedCheckbox.checked), + defaultCFG: !!(defaultCFGCheckbox && defaultCFGCheckbox.checked), + useDefaultDB: !!(dbCheckbox && dbCheckbox.checked) }); }); - // cancel + // --- Cancel Logic --- document.getElementById("btnCancel").addEventListener("click", () => { vscode.postMessage({ command: "cancel" }); }); diff --git a/src/vcastTestInterface.ts b/src/vcastTestInterface.ts index bf107ad5..e824708a 100644 --- a/src/vcastTestInterface.ts +++ b/src/vcastTestInterface.ts @@ -58,9 +58,13 @@ import { commandStatusType, executeCommandSync, executeVPythonScript, + executeWithRealTimeEchoWithProgress, } from "./vcastCommandRunner"; -import { checksumCommandToUse } from "./vcastInstallation"; +import { + checksumCommandToUse, + getVectorCastInstallationLocation, +} from "./vcastInstallation"; import { closeAnyOpenErrorFiles, @@ -71,6 +75,7 @@ import { closeConnection, globalEnviroDataServerActive, } from "../src-common/vcastServer"; +import { createNewCFGFromCompiler } from "./manage/manageSrc/manageUtils"; const fs = require("fs"); const path = require("path"); @@ -1197,3 +1202,152 @@ export async function getMCDCResultFile( return resultFile; } + +/** + * Updates databaseLocation setting + * @param vcShellPath Path to the defaultV VCDB + */ +export async function updateVCShellDatabase(vcShellPath: string) { + // Update Settings + const settings = vscode.workspace.getConfiguration("vectorcastTestExplorer"); + settings.update("databaseLocation", vcShellPath); + + vscode.window.showInformationMessage( + `Default Database location has been set to: ${vcShellPath}` + ); +} + +/** + * Updates CFG VCDB_FILENAME option + * @param vcShellPath Path of the vcshell db + * @param normalizedCFGPath Path to CFG + */ +export async function updateCFGWithVCShellDatabase( + vcShellPath: string, + normalizedCFGPath: string +) { + // Retrieve VectorCAST Dir + const vcDir = getVectorCastInstallationLocation(); + if (!vcDir) { + vectorMessage("VectorCAST Installation Location not found. Aborting."); + vscode.window.showWarningMessage( + `VectorCAST Installation Location not found. Aborting.` + ); + return; + } + + // Build paths + const vcShellCommand = path.join(normalizePath(vcDir), `clicast`); + const commandToRun = `${vcShellCommand}`; + const fileName = path.basename(vcShellPath); + const normalizedVCShellPath = normalizePath(vcShellPath); + + // Execute + const infoMessage = `Loading VectorCAST database from ${fileName} and setting it as the active VCDB.`; + await executeWithRealTimeEchoWithProgress( + commandToRun, + ["-lc", "option", "VCDB_FILENAME", `${normalizedVCShellPath}`], + normalizedCFGPath, + infoMessage + ); +} + +export interface ConfigurationOptions { + enableCodedTests: boolean; + defaultCFG: boolean; + useDefaultDB: boolean; +} + +/** + * Creates new CFG including selected options like: Default CFG, Enable Coded Tests + * @param workspaceRoot Root of Workspace + * @param compilerTag Compiler name + * @param configurationOptions Additional CFG options + */ +export async function createNewCFGFile( + targetDir: string, + compilerTag: string, + configurationOptions: ConfigurationOptions +) { + const vcDir = getVectorCastInstallationLocation(); + const commandToRun = path.join(vcDir, "clicast"); + + // Should not happen as we would get that message when initializing the extension, but to be sure + if (!vcDir) { + vectorMessage( + `Could not find VectorCAST Installation Location. Aborting creating a new CFG.` + ); + return; + } + + // Check if a CFG already exists + const cfgFile = path.join(targetDir, "CCAST_.CFG"); + if (fs.existsSync(cfgFile)) { + const choice = await vscode.window.showInformationMessage( + "A CFG file already exists at this location. Do you want to overwrite it?", + "Yes", + "Cancel" + ); + + if (choice !== "Yes") { + vscode.window.showInformationMessage( + `Operation cancelled. Existing CFG ( ${cfgFile} ) was not overwritten.` + ); + return; + } + } + + // First create new CFG and return path + const compilerPath = await createNewCFGFromCompiler(compilerTag, targetDir); + + // Set Coded Tests option + let codedFlag = "FALSE"; + if (configurationOptions.enableCodedTests) { + codedFlag = "TRUE"; + } + + const codedOptionArgs = [ + "-lc", + "option", + "VCAST_CODED_TESTS_SUPPORT", + codedFlag, + ]; + const codedOptionInfoMessage = + "Setting VCAST_CODED_TESTS_SUPPORT in CFG File"; + await executeWithRealTimeEchoWithProgress( + commandToRun, + codedOptionArgs, + targetDir, + codedOptionInfoMessage + ); + + // Set as Default CFG if required + if (configurationOptions.defaultCFG) { + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + settings.update( + "configurationLocation", + compilerPath, + vscode.ConfigurationTarget.Workspace + ); + } + + // Apply Default Database if requested and path exists + if (configurationOptions.useDefaultDB && compilerPath) { + const settings = vscode.workspace.getConfiguration( + "vectorcastTestExplorer" + ); + const dbPath = settings.get("databaseLocation"); + + if (dbPath && fs.existsSync(dbPath)) { + await updateCFGWithVCShellDatabase(dbPath, path.dirname(compilerPath)); + } else { + vscode.window.showWarningMessage( + "Could not set Default Database: The database file defined in settings could not be found." + ); + } + } + vectorMessage(`CCAST_.CFG created in ${path.dirname(compilerPath)}`); + return compilerPath; +} diff --git a/tests/internal/e2e/test/manage/tutorial/CCAST_.CFG b/tests/internal/e2e/test/manage/tutorial/CCAST_.CFG new file mode 100644 index 00000000..ccc701a8 --- /dev/null +++ b/tests/internal/e2e/test/manage/tutorial/CCAST_.CFG @@ -0,0 +1,2 @@ +VCAST_DISPLAY_UNINST_EXPR: FALSE +VCAST_REPOSITORY: /home/denis/VectorCAST/vcshell_implementation/tests/internal/e2e/test/vcastTutorial diff --git a/tests/internal/e2e/test/manage/tutorial/vcshell.db b/tests/internal/e2e/test/manage/tutorial/vcshell.db new file mode 100644 index 00000000..34ef56ad Binary files /dev/null and b/tests/internal/e2e/test/manage/tutorial/vcshell.db differ diff --git a/tests/internal/e2e/test/specs/vcast_manage.test.ts b/tests/internal/e2e/test/specs/vcast_manage.test.ts index 0b462eec..2b10ccdb 100644 --- a/tests/internal/e2e/test/specs/vcast_manage.test.ts +++ b/tests/internal/e2e/test/specs/vcast_manage.test.ts @@ -91,20 +91,12 @@ describe("vTypeCheck VS Code Extension", () => { it("testing creating new compiler", async () => { await updateTestID(); - await bottomBar.toggle(true); - const outputView = await bottomBar.openOutputView(); - await outputView.clearText(); const notificationsCenter = await workbench.openNotificationsCenter(); await notificationsCenter.clearAllNotifications(); console.log("Create new Compiler in Project"); - await executeContextMenuAction( - 0, - "Test.vcm", - true, - "Create new Compiler in Project" - ); + await executeContextMenuAction(0, "Test.vcm", true, "Create new CFG"); console.log("Inserting Data to Webview"); await insertStringIntoAutocompletionInput( @@ -112,11 +104,12 @@ describe("vTypeCheck VS Code Extension", () => { "Compiler Name Input", true ); + const outputView = await bottomBar.openOutputView(); await browser.waitUntil( async () => (await outputView.getText()) .toString() - .includes(`Added Compiler CCAST_.CFG to Project Test`), + .includes("has been added into project"), { timeout: TIMEOUT } ); @@ -137,7 +130,7 @@ describe("vTypeCheck VS Code Extension", () => { const notificationsCenter = await workbench.openNotificationsCenter(); await notificationsCenter.clearAllNotifications(); - console.log("Executing Create New Project Command:"); + console.log("Executing Create New Project Command (with options = false):"); await browser.executeWorkbench((vscode) => { vscode.commands.executeCommand("vectorcastTestExplorer.createNewProject"); }); @@ -151,13 +144,23 @@ describe("vTypeCheck VS Code Extension", () => { await browser.keys("GNU Native_Automatic_C++17"); await browser.keys(["Tab"]); await browser.keys(["Tab"]); + await browser.keys(["Tab"]); + await browser.keys(["Tab"]); await browser.keys(["Enter"]); await browser.waitUntil( async () => (await outputView.getText()) .toString() - .includes(`Added Compiler CCAST_.CFG to Project ANewProject`), + .includes(`-lc option VCAST_CODED_TESTS_SUPPORT FALSE`), + { timeout: TIMEOUT } + ); + + await browser.waitUntil( + async () => + (await outputView.getText()) + .toString() + .includes(`Processing project: ANewProject`), { timeout: TIMEOUT } ); @@ -171,6 +174,120 @@ describe("vTypeCheck VS Code Extension", () => { expect(compilerNode).toBeDefined(); }); + it("testing setting default vcshell.db", async () => { + await updateTestID(); + await bottomBar.toggle(true); + const outputView = await bottomBar.openOutputView(); + await outputView.clearText(); + + const workbench = await browser.getWorkbench(); + const activityBar = workbench.getActivityBar(); + const explorerView = await activityBar.getViewControl("Explorer"); + await explorerView?.openView(); + + const workspaceFolderSection = + await expandWorkspaceFolderSectionInExplorer("vcastTutorial"); + const vcshellFolder = workspaceFolderSection.findItem("tutorial"); + await (await vcshellFolder).select(); + + console.log("Selecting vcshell.db"); + const vcshell = await workspaceFolderSection.findItem("vcshell.db"); + await executeCtrlClickOn(vcshell); + await releaseCtrl(); + console.log("Executing: Set as default Database"); + await vcshell.openContextMenu(); + await (await $("aria/VectorCAST: Set as default Database")).click(); + + console.log("Checking whetehr Setting got updated"); + const settingsEditor = await workbench.openSettings(); + + // Switch scope to Workspace + await (await $("aria/Workspace")).click(); + + const databaseLocationSetting = await settingsEditor.findSetting( + "Database Location", + "Vectorcast Test Explorer" + ); + + const locationValue = await databaseLocationSetting.getValue(); + console.log(`Location Value: ${locationValue}`); + expect(locationValue).toBeDefined(); + // Close settings editor so the output panel is accessible in the next test + await workbench.getEditorView().closeAllEditors(); + }); + + it("testing creating second project 'Banana'", async () => { + await updateTestID(); + await bottomBar.toggle(true); + const outputView = await bottomBar.openOutputView(); + await outputView.clearText(); + + const notificationsCenter = await workbench.openNotificationsCenter(); + await notificationsCenter.clearAllNotifications(); + + console.log("Executing Create New Project Command for Banana:"); + await browser.executeWorkbench((vscode) => { + vscode.commands.executeCommand("vectorcastTestExplorer.createNewProject"); + }); + + console.log("Inserting Data to Webview for Banana project"); + await insertStringToInput("Banana", "Project Name Input"); + await browser.keys(["Tab"]); + await browser.keys("GNU Native_Automatic_C++"); + await browser.keys(["Tab"]); + await browser.keys([" "]); + await browser.keys(["Tab"]); + await browser.keys([" "]); + await browser.keys(["Tab"]); + await browser.keys([" "]); + await browser.keys(["Tab"]); + await browser.keys(["Tab"]); + await browser.keys(["Enter"]); + + // Switch back to the main VS Code frame — we're still inside the webview iframe + await browser.switchToFrame(null); + await browser.pause(500); + + // Force the bottom panel into existence via keyboard shortcut + // bottomBar.toggle() fails if the panel DOM element was never rendered + await browser.keys(["Control", "j"]); + await browser.pause(300); + + await bottomBar.toggle(true); + const freshOutputView = await bottomBar.openOutputView(); + + await browser.waitUntil( + async () => + (await freshOutputView.getText()) + .toString() + .includes(`Processing project: Banana`), + { timeout: TIMEOUT } + ); + console.log("Checking existence of Banana Project"); + const projectNode = await findTreeNodeAtLevel(0, "Banana.vcm"); + const compilerNode = await findTreeNodeAtLevel( + 1, + "GNU_Native_Automatic_C++" + ); + expect(projectNode).toBeDefined(); + expect(compilerNode).toBeDefined(); + + console.log( + "Verifying configurationLocation setting ends with Banana/CCAST.CFG" + ); + const configLocation = await browser.executeWorkbench((vscode) => { + return vscode.workspace + .getConfiguration("vectorcastTestExplorer") + .get("configurationLocation"); + }); + + console.log(`Configuration location: ${configLocation}`); + expect(configLocation).toBeDefined(); + + // Check if config location is set with the new CFG from the project + expect(configLocation.endsWith("Banana/CCAST_.CFG")).toBe(true); + }); + it("testing tree structure", async () => { await updateTestID(); @@ -304,6 +421,8 @@ describe("vTypeCheck VS Code Extension", () => { const outputView = await bottomBar.openOutputView(); await outputView.clearText(); const initialWorkdir = process.env.INIT_CWD; + const workbench = await browser.getWorkbench(); + await workbench.getEditorView().closeAllEditors(); const testInputManage = path.join( initialWorkdir, @@ -492,7 +611,7 @@ describe("vTypeCheck VS Code Extension", () => { // CLose Report again await webview.close(); - await editorView.closeEditor("VectorCAST Report", 1); + await workbench.getEditorView().closeAllEditors(); }); it("testing creating a Testsuite", async () => { @@ -578,7 +697,7 @@ describe("vTypeCheck VS Code Extension", () => { expect(testsuiteNode).toBeUndefined(); }); - it("testing deleting a project Environment", async () => { + it("testing cleaning a project Environment", async () => { await updateTestID(); await bottomBar.toggle(true); const outputView = await bottomBar.openOutputView(); @@ -725,7 +844,7 @@ describe("vTypeCheck VS Code Extension", () => { const notificationsCenter = await workbench.openNotificationsCenter(); await notificationsCenter.clearAllNotifications(); - console.log("Deleting Environment QUACK from Project"); + // Trigger the Delete action await executeContextMenuAction( 3, "QUACK", @@ -734,13 +853,25 @@ describe("vTypeCheck VS Code Extension", () => { ); console.log("Confirming Notifications to delete the Environment"); - const notifications = await $("aria/Notifications"); - await notifications.click(); - const vcastNotificationSourceElement = await $( - "aria/VectorCAST Test Explorer (Extension)" - ); - const vcastNotification = await vcastNotificationSourceElement.$(".."); - await (await vcastNotification.$("aria/Delete")).click(); + + // Wait for the notification to be generated (exist in DOM) + const vcastNotificationSelector = + "aria/VectorCAST Test Explorer (Extension)"; + const pendingNotification = await $(vcastNotificationSelector); + await browser.takeScreenshot(); + await browser.saveScreenshot("info_clicked_on_delete.png"); + + // Navigate to the Delete button + const vcastNotification = await $(vcastNotificationSelector).$(".."); + const deleteAction = await vcastNotification.$("aria/Delete"); + + // Wait for the button to be Clickable (handles animation/obscuring) + await deleteAction.waitForClickable({ + timeout: TIMEOUT, + timeoutMsg: "Delete button in notification was not interactable", + }); + + await deleteAction.click(); console.log("Checking for Output logs if Environment deletion is finished"); await browser.waitUntil( @@ -790,8 +921,6 @@ describe("vTypeCheck VS Code Extension", () => { const workspaceFolderSection = await expandWorkspaceFolderSectionInExplorer("vcastTutorial"); - const cppFolder = workspaceFolderSection.findItem("tutorial"); - await (await cppFolder).select(); console.log("Selecting database.cpp & manager.cpp"); const managerCpp = await workspaceFolderSection.findItem("manager.cpp"); diff --git a/tests/internal/e2e/test/test_utils/vcast_utils.ts b/tests/internal/e2e/test/test_utils/vcast_utils.ts index 7787c307..9a194952 100644 --- a/tests/internal/e2e/test/test_utils/vcast_utils.ts +++ b/tests/internal/e2e/test/test_utils/vcast_utils.ts @@ -1873,9 +1873,30 @@ export async function insertStringIntoAutocompletionInput( } if (shouldTabToCreate) { + await browser.keys(["Tab"]); + await browser.keys(["Tab"]); await browser.keys(["Tab"]); await browser.keys(["Tab"]); await browser.keys(["Enter"]); + + // Exit the webview iframe context before interacting with VS Code UI + await webview.close(); + + // Handle "CFG already exists" notification if it appears + console.log("Handling CFG overwrite notification"); + try { + const overwriteNotificationSelector = + "aria/VectorCAST Test Explorer (Extension)"; + const pendingNotification = await $(overwriteNotificationSelector); + await pendingNotification.waitForExist({ timeout: 3000 }); + const notification = await $(overwriteNotificationSelector).$(".."); + const yesAction = await notification.$("aria/Yes"); + await yesAction.waitForClickable({ timeout: 3000 }); + await yesAction.click(); + } catch { + // Notification didn't appear, CFG didn't exist yet — that's fine + } + return true; } else { // Dismiss suggestions / blur