@salesforce/salesforcedx-vscode-test-tools
Version:
Test automation framework for Salesforce Extensions for VS Code
231 lines (201 loc) • 8.76 kB
text/typescript
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { EnvironmentSettings as Env } from '../../src/environmentSettings';
import { TestSetup } from '../../src/testSetup';
import { log, pause, Duration, transformedUserName } from '../core/miscellaneous';
import { getStatusBarItemWhichIncludes } from '../ui-interaction/statusBar';
import {
orgList,
orgLoginSfdxUrl,
runCliCommand,
scratchOrgCreate
} from '../system-operations/cliCommands';
import { attemptToFindOutputPanelText, executeQuickPick } from '../ui-interaction';
import { verifyNotificationWithRetry, retryOperation } from '../retryUtils';
/**
* Sets up a scratch org for testing
* @param testSetup - The test setup configuration
* @param scratchOrgEdition - The edition of the scratch org to create
*/
export async function setUpScratchOrg(testSetup: TestSetup) {
await authorizeDevHub(testSetup);
return await createDefaultScratchOrgViaCli(testSetup);
}
/**
* Authorizes a DevHub for use in tests
* @param testSetup - The test setup configuration
* @throws Error if DevHub alias or username are not set
*/
export async function authorizeDevHub(testSetup: TestSetup): Promise<void> {
log('');
log(`${testSetup.testSuiteSuffixName} - Starting authorizeDevHub()...`);
// Only need to check this once.
if (!testSetup.aliasAndUserNameWereVerified) {
await verifyAliasAndUserName();
testSetup.aliasAndUserNameWereVerified = true;
}
// This is essentially the "SFDX: Authorize a Dev Hub" command, but using the CLI and an auth file instead of the UI.
if (!testSetup.projectFolderPath) {
throw new Error('Project folder path is not set');
}
// Call org:login:sfdx-url and read in the JSON that was just created.
log(`${testSetup.testSuiteSuffixName} - calling sf org:login:sfdx-url...`);
await orgLoginSfdxUrl();
log(`${testSetup.testSuiteSuffixName} - ...finished authorizeDevHub()`);
log('');
}
/**
* Verifies that the alias and user name are set and match an org in the org list
* @throws Error if DevHub alias or username are not set or can't be found in org list
*/
export async function verifyAliasAndUserName() {
const environmentSettings = Env.getInstance();
const devHubAliasName = environmentSettings.devHubAliasName;
if (!devHubAliasName) {
throw new Error('Error: devHubAliasName was not set.');
}
const devHubUserName = environmentSettings.devHubUserName;
if (!devHubUserName) {
throw new Error('Error: devHubUserName was not set.');
}
const execResult = await orgList();
const sfOrgListResult = JSON.parse(execResult.stdout).result;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const nonScratchOrgs = sfOrgListResult.nonScratchOrgs as any[];
for (let i = 0; i < nonScratchOrgs.length; i++) {
const nonScratchOrg = nonScratchOrgs[i];
if (nonScratchOrg.alias === devHubAliasName && nonScratchOrg.username === devHubUserName) {
return;
}
}
throw new Error(
`Error: matching devHub alias '${devHubAliasName}' and devHub user name '${devHubUserName}' was not found.\nPlease consult README.md and make sure DEV_HUB_ALIAS_NAME and DEV_HUB_USER_NAME are set correctly.`
);
}
const buildAlias = () => {
const currentDate = new Date();
const ticks = currentDate.getTime();
const day = ('0' + currentDate.getDate()).slice(-2);
const month = ('0' + (currentDate.getMonth() + 1)).slice(-2);
const year = currentDate.getFullYear();
const currentOsUserName = transformedUserName();
return `TempScratchOrg_${year}_${month}_${day}_${currentOsUserName}_${ticks}_OrgAuth`;
};
export const createDefaultScratchOrgViaCli = async (testSetup: TestSetup): Promise<TestSetup> => {
const scratchOrgAliasName = buildAlias();
testSetup.scratchOrgAliasName = scratchOrgAliasName;
const orgCreateResult = await scratchOrgCreate('developer', 'NONE', testSetup.scratchOrgAliasName, 1);
if (orgCreateResult.exitCode > 0) {
throw new Error(
`Error: creating scratch org failed with exit code ${orgCreateResult.exitCode}\n stderr ${orgCreateResult.stderr}`
);
}
testSetup.scratchOrgId = JSON.parse(orgCreateResult.stdout).result.id;
return testSetup;
};
/**
* Creates a default scratch org for testing
* @param testSetup - The test setup configuration
* @param edition - The edition of the scratch org to create (defaults to 'developer')
* @throws Error if scratch org creation fails
* @private
*/
export async function createDefaultScratchOrg(): Promise<string> {
log('Creating a default scratch org...');
return await retryOperation(async () => {
const prompt = await executeQuickPick(
'SFDX: Create a Default Scratch Org...',
Duration.seconds(1)
);
// Select a project scratch definition file (config/project-scratch-def.json)
await prompt.confirm();
// Enter an org alias - yyyy-mm-dd-username-ticks
const currentDate = new Date();
const ticks = currentDate.getTime();
const day = ('0' + currentDate.getDate()).slice(-2);
const month = ('0' + (currentDate.getMonth() + 1)).slice(-2);
const year = currentDate.getFullYear();
const currentOsUserName = transformedUserName();
const scratchOrgAliasName = `TempScratchOrg_${year}_${month}_${day}_${currentOsUserName}_${ticks}_OrgAuth`;
await prompt.setText(scratchOrgAliasName);
await pause(Duration.seconds(1));
// Press Enter/Return.
await prompt.confirm();
// Enter the number of days.
await prompt.setText('1');
await pause(Duration.seconds(1));
// Press Enter/Return.
await prompt.confirm();
const successNotificationWasFound = await verifyNotificationWithRetry(
/SFDX: Create a Default Scratch Org\.\.\. successfully ran/,
);
if (successNotificationWasFound !== true) {
const failureNotificationWasFound = await verifyNotificationWithRetry(
/SFDX: Create a Default Scratch Org\.\.\. failed to run/,
);
if (failureNotificationWasFound === true) {
if (
await attemptToFindOutputPanelText(
'Salesforce Org Management',
'organization has reached its daily scratch org signup limit',
5
)
) {
// This is a known issue...
log('Warning - creating the scratch org failed, but the failure was due to the daily signup limit');
} else if (await attemptToFindOutputPanelText('Salesforce Org Management', 'is enabled as a Dev Hub', 5)) {
// This is a known issue...
log('Warning - Make sure that the org is enabled as a Dev Hub.');
log(
'Warning - To enable it, open the org in your browser, navigate to the Dev Hub page in Setup, and click Enable.'
);
log(
'Warning - If you still see this error after enabling the Dev Hub feature, then re-authenticate to the org.'
);
} else {
// The failure notification is showing, but it's not due to maxing out the daily limit. What to do...?
log('Warning - creating the scratch org failed... not sure why...');
}
} else {
log(
'Warning - creating the scratch org failed... neither the success notification or the failure notification was found.'
);
}
// Throw error to trigger retry if success notification was not found
throw new Error('Scratch org creation failed - success notification not found');
}
// Look for the org's alias name in the list of status bar items.
const scratchOrgStatusBarItem = await getStatusBarItemWhichIncludes(scratchOrgAliasName);
if (!scratchOrgStatusBarItem) {
throw new Error('Scratch org status bar item not found');
}
return scratchOrgAliasName;
}, 3, 'Failed to create default scratch org');
}
/**
* Deletes scratch org information from the DevHub
* @param testSetup - The test setup configuration
* @throws Error if deletion fails
*/
export async function deleteScratchOrgInfo(testSetup: TestSetup): Promise<void> {
if (testSetup.scratchOrgId) {
const sfDataDeleteRecord = await runCliCommand(
'data:delete:record',
'--sobject',
'ScratchOrgInfo',
'--where',
`ScratchOrg=${testSetup.scratchOrgId.slice(0, -3)}`,
'--target-org',
Env.getInstance().devHubAliasName
);
if (sfDataDeleteRecord.exitCode > 0) {
const message = `data delete record failed with exit code ${sfDataDeleteRecord.exitCode}\n stderr ${sfDataDeleteRecord.stderr}`;
log(message);
throw new Error(message);
}
}
}