Skip to content
This repository was archived by the owner on Jul 2, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
93d8ce8
feat: add new function replaceLineInFile() in textEditorView.ts
daphne-sfdc Jul 24, 2025
3ea24f7
chore(release): 1.0.1-dev.132 [skip ci]
svc-idee-bot Jul 24, 2025
37199ab
ci: fix publish scripts to use correct tag
daphne-sfdc Jul 24, 2025
c099312
chore(release): 1.0.1-dev.133 [skip ci]
svc-idee-bot Jul 24, 2025
b308d11
feat: createApexClass() can be used to create default Apex classes wi…
daphne-sfdc Jul 24, 2025
ca01c35
chore(release): 1.0.1-dev.134 [skip ci]
svc-idee-bot Jul 24, 2025
031544f
feat: createApexClass() uses fs.writeFile() instead of command palette
daphne-sfdc Jul 24, 2025
01fdd20
chore(release): 1.0.1-dev.135 [skip ci]
svc-idee-bot Jul 24, 2025
53267b8
feat: createAnonymousApexFile() uses fs.writeFile()
daphne-sfdc Jul 24, 2025
bdeff32
fix: createApexClass() and functions that call it should take in the …
daphne-sfdc Jul 25, 2025
d38613d
chore(release): 1.0.1-dev.136 [skip ci]
svc-idee-bot Jul 25, 2025
23f6152
feat: overrideTextInFile() uses fs.writeFile()
daphne-sfdc Jul 25, 2025
decd599
chore(release): 1.0.1-dev.137 [skip ci]
svc-idee-bot Jul 25, 2025
dc79dd0
fix: open the Apex class in the Editor View after it is created
daphne-sfdc Jul 25, 2025
5d81143
chore(release): 1.0.1-dev.138 [skip ci]
svc-idee-bot Jul 25, 2025
d62ef5c
chore: resolve 'JSDoc types may be moved to TypeScript types. ts(8000…
daphne-sfdc Jul 25, 2025
cd66963
chore: fix typo
daphne-sfdc Jul 25, 2025
46206d0
feat: createVisualforcePage() uses fs.writeFile()
daphne-sfdc Jul 25, 2025
d4022f2
feat: createLwc() and createAura() use fs.writeFile()
daphne-sfdc Jul 25, 2025
6c74661
chore(release): 1.0.1-dev.139 [skip ci]
svc-idee-bot Jul 25, 2025
70fc5c8
fix: create folder before creating files if folder does not exist in …
daphne-sfdc Jul 25, 2025
83beca5
chore(release): 1.0.1-dev.140 [skip ci]
svc-idee-bot Jul 25, 2025
85722cf
fix: use existing UI-based approach in overrideTextInFile() if save =…
daphne-sfdc Jul 26, 2025
c7f31da
chore(release): 1.0.1-dev.141 [skip ci]
svc-idee-bot Jul 26, 2025
9b9d633
fix: focus active editor group before running deploy/retrieve in runA…
daphne-sfdc Jul 26, 2025
055b21c
chore(release): 1.0.1-dev.142 [skip ci]
svc-idee-bot Jul 26, 2025
e726125
fix: focus active editor group before replacing file contents via UI …
daphne-sfdc Jul 26, 2025
69dc399
chore(release): 1.0.1-dev.143 [skip ci]
svc-idee-bot Jul 26, 2025
cf7030f
chore: add pause
daphne-sfdc Jul 26, 2025
73d4838
chore(release): 1.0.1-dev.144 [skip ci]
svc-idee-bot Jul 26, 2025
7ec7dc1
chore: add numbered logging
daphne-sfdc Jul 26, 2025
da753a5
chore(release): 1.0.1-dev.145 [skip ci]
svc-idee-bot Jul 26, 2025
244bd72
try: fallback for textEditor.sendKeys()
daphne-sfdc Jul 26, 2025
2a9684d
chore(release): 1.0.1-dev.146 [skip ci]
svc-idee-bot Jul 26, 2025
f140125
try: multiple fallback selectors
daphne-sfdc Jul 26, 2025
a6010a3
chore(release): 1.0.1-dev.147 [skip ci]
svc-idee-bot Jul 26, 2025
0186884
try: pause for 2 seconds before editorElement.sendKeys()
daphne-sfdc Jul 28, 2025
ea91e47
chore(release): 1.0.1-dev.148 [skip ci]
svc-idee-bot Jul 28, 2025
b69414d
try: focus active editor group before writing using fallback
daphne-sfdc Jul 28, 2025
b2c2370
chore(release): 1.0.1-dev.149 [skip ci]
svc-idee-bot Jul 28, 2025
dc0c600
fix: remove option to not save the file in overrideTextInFile() becau…
daphne-sfdc Jul 28, 2025
17c17a7
chore(release): 1.0.1-dev.150 [skip ci]
svc-idee-bot Jul 28, 2025
c9c96a7
fix: createAnonymousApexFile() takes in folder as an argument
daphne-sfdc Jul 28, 2025
1e1ddcb
chore(release): 1.0.1-dev.151 [skip ci]
svc-idee-bot Jul 28, 2025
27f5825
fix: open the anonymous Apex file in Editor View after creating it
daphne-sfdc Jul 28, 2025
84072ec
chore(release): 1.0.1-dev.152 [skip ci]
svc-idee-bot Jul 28, 2025
871a1e2
Merge branch 'main' into daphne/W-19111839-fs-write-for-e2e-tests
daphne-sfdc Jul 29, 2025
2c04f0d
Merge branch 'main' into daphne/W-19111839-fs-write-for-e2e-tests
daphne-sfdc Jul 30, 2025
066c002
chore(release): 1.1.7-dev.0 [skip ci]
svc-idee-bot Jul 30, 2025
c848c81
fix: use openFile() instead of getTextEditor() to open the newly crea…
daphne-sfdc Jul 31, 2025
89f6c89
chore(release): 1.1.7-dev.1 [skip ci]
svc-idee-bot Jul 31, 2025
c030d77
Merge branch 'main' into daphne/W-19111839-fs-write-for-e2e-tests
daphne-sfdc Jul 31, 2025
aeff426
chore: cleanup
daphne-sfdc Jul 31, 2025
e27ea2a
chore: move cspell custom dictionary words from settings.json to cspe…
daphne-sfdc Jul 31, 2025
88f1ab7
refactor: use new helper function createOrOverwriteFile()
daphne-sfdc Aug 1, 2025
6bb8831
chore(release): 1.1.7-dev.2 [skip ci]
svc-idee-bot Aug 1, 2025
4a0d44f
chore: consistent fs/promises import
daphne-sfdc Aug 4, 2025
307d864
chore(release): 1.1.7-dev.3 [skip ci]
svc-idee-bot Aug 4, 2025
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 .github/workflows/onRelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
getDistTag:
outputs:
tag: ${{ steps.distTag.outputs.tag }}
gitTag: ${{ github.event.release.tag_name || inputs.tag }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@
120
],
"cSpell.words": [
"auradoc",
"inputarea",
"salesforcedx",
"SFDC",
"SFDX",
"slds",
"Sobject",
"SOQL",
"visualforce"
]
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@salesforce/salesforcedx-vscode-test-tools",
"version": "1.1.6",
"version": "1.1.7-dev.1",
"description": "Test automation framework for Salesforce Extensions for VS Code",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/core/miscellaneous.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ export async function openFolder(path: string) {
}

/**
* An definite alternative of getTextEditor to open a file in text editor
* A definite alternative of getTextEditor to open a file in text editor
* @param path
*/
export async function openFile(path: string) {
Expand Down
133 changes: 78 additions & 55 deletions src/salesforce-components/apexUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,74 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { executeQuickPick } from '../ui-interaction/commandPrompt';
import { Duration, log, pause } from '../core/miscellaneous';
import fs from 'fs/promises';
import { join } from 'path';
import { Duration, log, openFile, pause } from '../core/miscellaneous';
import { getWorkbench } from '../ui-interaction/workbench';
import { getTextEditor, overrideTextInFile } from '../ui-interaction/textEditorView';
import { InputBox, QuickOpenBox } from 'vscode-extension-tester';
import { retryOperation } from '../retryUtils';
import { clickButtonOnModalDialog } from '../ui-interaction';
import { getTextEditor } from '../ui-interaction/textEditorView';

/**
* Creates an Apex class with the specified name and content
* @param name - The name of the Apex class to create
* @param classText - The content of the Apex class
* @param breakpoint - Optional line number where a breakpoint should be set
*/
export async function createApexClass(name: string, classText: string, breakpoint?: number): Promise<void> {
export async function createApexClass(name: string, folder: string, classText?: string, breakpoint?: number): Promise<void> {
log(`calling createApexClass(${name})`);
let inputBox: InputBox | QuickOpenBox;
await retryOperation(async () => {
// Using the Command palette, run SFDX: Create Apex Class to create the main class
inputBox = await executeQuickPick('SFDX: Create Apex Class', Duration.seconds(2));
});

// Set the name of the new Apex Class
await retryOperation(async () => {
await inputBox.setText(name);
await pause(Duration.seconds(1));
await inputBox.confirm();
await pause(Duration.seconds(1));
await inputBox.confirm();
await pause(Duration.seconds(1));
await clickButtonOnModalDialog('Overwrite', false);
await pause(Duration.seconds(1));
});

log(`Blank Apex Class ${name} created successfully.`);
// Modify class content
const workbench = getWorkbench();
log('Getting text editor for the new Apex Class');
const textEditor = await getTextEditor(workbench, name + '.cls');
log('Done getting text editor for the new Apex Class');
await pause(Duration.seconds(1));
log(`Setting text for Apex Class ${name}`);
await overrideTextInFile(textEditor, classText);
log(`Done setting text for Apex Class ${name}`);

// Use provided classText or default template
const content = classText || [
`public with sharing class ${name} {`,
`\tpublic ${name}() {`,
``,
`\t}`,
`}`
].join('\n');

// Define metadata content
const metaContent = [
`<?xml version="1.0" encoding="UTF-8"?>`,
`<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">`,
` <apiVersion>64.0</apiVersion>`,
` <status>Active</status>`,
`</ApexClass>`
].join('\n');

// Create the file paths (assuming we're in a Salesforce project structure)
const filePath = join(folder, `${name}.cls`);
const metaFilePath = join(folder, `${name}.cls-meta.xml`);

try {
// Ensure the folder exists before writing files
await fs.mkdir(folder, { recursive: true });

// Write the Apex class file using fs.writeFile
await fs.writeFile(filePath, content, 'utf8');
log(`Apex Class ${name} created successfully at ${filePath}`);

// Write the metadata file using fs.writeFile
await fs.writeFile(metaFilePath, metaContent, 'utf8');
log(`Apex Class metadata ${name}.cls-meta.xml created successfully at ${metaFilePath}`);
} catch (error) {
log(`Error creating Apex Class ${name}: ${error}`);
throw error;
}

// Open the file in the text editor
await pause(Duration.seconds(1));
await openFile(filePath);

// Handle breakpoint if specified
if (breakpoint) {
const workbench = getWorkbench();
const textEditor = await getTextEditor(workbench, name + '.cls');
log('createApexClass() - Setting breakpoints');
await pause(Duration.seconds(5)); // wait for file to be saved and loaded

log(`createApexClass() - Set breakpoint ${breakpoint}`);
await textEditor.toggleBreakpoint(breakpoint);
}

await pause(Duration.seconds(1));
log(`Apex Class ${name} modified successfully.`);
}
Expand All @@ -64,7 +81,7 @@ export async function createApexClass(name: string, classText: string, breakpoin
* Creates an Apex class and its corresponding test class
* @param name - The name of the Apex class (test class will be named [name]Test)
*/
export async function createApexClassWithTest(name: string): Promise<void> {
export async function createApexClassWithTest(name: string, folder: string): Promise<void> {
log(`calling createApexClassWithTest()`);
const classText = [
`public with sharing class ${name} {`,
Expand All @@ -73,7 +90,7 @@ export async function createApexClassWithTest(name: string): Promise<void> {
`\t}`,
`}`
].join('\n');
await createApexClass(name, classText, 3);
await createApexClass(name, folder, classText, 3);

const testText = [
`@IsTest`,
Expand All @@ -87,14 +104,14 @@ export async function createApexClassWithTest(name: string): Promise<void> {
`\t}`,
`}`
].join('\n');
await createApexClass(name + 'Test', testText, 6);
await createApexClass(name + 'Test', folder, testText, 6);
}

/**
* Creates an Account service Apex class with a bug and its corresponding test class
* The bug is that the TickerSymbol is set to accountNumber instead of tickerSymbol
*/
export async function createApexClassWithBugs(): Promise<void> {
export async function createApexClassWithBugs(folder: string): Promise<void> {
log(`calling createApexClassWithBugs()`);
const classText = [
`public with sharing class AccountService {`,
Expand All @@ -108,7 +125,7 @@ export async function createApexClassWithBugs(): Promise<void> {
`\t}`,
`}`
].join('\n');
await createApexClass('AccountService', classText);
await createApexClass('AccountService', folder, classText);

const testText = [
`@IsTest`,
Expand All @@ -131,35 +148,41 @@ export async function createApexClassWithBugs(): Promise<void> {
`\t}`,
`}`
].join('\n');
await createApexClass('AccountServiceTest', testText);
await createApexClass('AccountServiceTest', folder, testText);
}

/**
* Creates an anonymous Apex file with a simple debug statement
*/
export async function createAnonymousApexFile(): Promise<void> {
export async function createAnonymousApexFile(folder: string): Promise<void> {
log(`calling createAnonymousApexFile()`);
const workbench = getWorkbench();

// Using the Command palette, run File: New File...
const inputBox = await executeQuickPick('Create: New File...', Duration.seconds(1));
// Define the file content
const fileContent = ["System.debug('¡Hola mundo!');", ''].join('\n');

// Set the name of the new Anonymous Apex file
await inputBox.setText('Anonymous.apex');
await inputBox.confirm();
await inputBox.confirm();
// Create the file path in the project root
const filePath = join(folder, 'Anonymous.apex');

try {
// Write the Anonymous Apex file using fs.writeFile
await fs.writeFile(filePath, fileContent, 'utf8');
log(`Anonymous Apex file created successfully at ${filePath}`);
} catch (error) {
log(`Error creating Anonymous Apex file: ${error}`);
throw error;
}

// Open the file in the text editor
await pause(Duration.seconds(1));
await openFile(filePath);

const textEditor = await getTextEditor(workbench, 'Anonymous.apex');
const fileContent = ["System.debug('¡Hola mundo!');", ''].join('\n');
await textEditor.setText(fileContent);
await textEditor.save();
await pause(Duration.seconds(1));
}

/**
* Creates an Apex controller class for Visualforce pages
*/
export async function createApexController(): Promise<void> {
export async function createApexController(folder: string): Promise<void> {
log(`calling createApexController()`);
const classText = [
`public class MyController {`,
Expand All @@ -177,5 +200,5 @@ export async function createApexController(): Promise<void> {
`\t}`,
`}`
].join('\n');
await createApexClass('MyController', classText);
await createApexClass('MyController', folder, classText);
}
3 changes: 2 additions & 1 deletion src/salesforce-components/deployAndRetrieve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const runAndValidateCommand = async (
prefix?: string
): Promise<void> => {
log(`runAndValidateCommand()`);
await executeQuickPick('View: Focus Active Editor Group', Duration.seconds(1));
await executeQuickPick(`SFDX: ${operation} This Source ${fromTo} Org`, Duration.seconds(5));

await validateCommand(operation, fromTo, operationType, metadataType, [fullName], prefix);
Expand Down Expand Up @@ -128,7 +129,7 @@ const constructExpectedTexts = (

const metadataPaths: Record<string, string> = {
ApexClass: `force-app${pathSeparator}main${pathSeparator}default${pathSeparator}classes`,
ExternalServiceRsegistration: `force-app${pathSeparator}main${pathSeparator}default${pathSeparator}externalServiceRegistrations`,
ExternalServiceRegistration: `force-app${pathSeparator}main${pathSeparator}default${pathSeparator}externalServiceRegistrations`,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Found this typo - how had this been working before?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's weird. Good catch!

CustomObject: `force-app${pathSeparator}main${pathSeparator}default${pathSeparator}objects`,
CustomField: `force-app${pathSeparator}main${pathSeparator}default${pathSeparator}objects`
};
Expand Down
Loading
Loading