diff --git a/.mocharc.json b/.mocharc.json index 904f106..5d3aba1 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -1,4 +1,4 @@ { - "timeout": 240000, + "timeout": 300000, "reporter": "./lib/custom-summary-reporter.js" } diff --git a/test/selfSpecs/notifications.test.ts b/test/selfSpecs/notifications.test.ts index cece640..b9bbdf0 100644 --- a/test/selfSpecs/notifications.test.ts +++ b/test/selfSpecs/notifications.test.ts @@ -37,7 +37,7 @@ describe('Notifications', async () => { utilities.Duration.seconds(2) ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(isPresent).to.be.true; + expect(isPresent).to.equal(true); await utilities.dismissNotification('Modify the file and retrieve again'); await utilities.pause(utilities.Duration.seconds(1)); const isNotPresent = await utilities.notificationIsAbsentWithTimeout( @@ -45,7 +45,7 @@ describe('Notifications', async () => { utilities.Duration.seconds(1) ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(isNotPresent).to.be.true; + expect(isNotPresent).to.equal(true); await utilities.pause(utilities.Duration.seconds(2)); }); it('should show a notification with two actions', async () => { @@ -55,7 +55,7 @@ describe('Notifications', async () => { utilities.Duration.seconds(1) ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(isPresent).to.be.true; + expect(isPresent).to.equal(true); await utilities.pause(utilities.Duration.seconds(1)); await utilities.acceptNotification('Choose an action:', 'A', utilities.Duration.seconds(1)); @@ -65,6 +65,6 @@ describe('Notifications', async () => { utilities.Duration.seconds(5) ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(isNotPresent).to.be.true; + expect(isNotPresent).to.equal(true); }); }); diff --git a/test/specs/runApexTests.e2e.ts b/test/specs/runApexTests.e2e.ts new file mode 100644 index 0000000..30de4dd --- /dev/null +++ b/test/specs/runApexTests.e2e.ts @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2023, 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 { step } from 'mocha-steps'; +import { By, InputBox, QuickOpenBox, SideBarView, after } from 'vscode-extension-tester'; +import { TestSetup } from '../testSetup'; +import * as utilities from '../utilities/index'; +import { expect } from 'chai'; + +describe('Run Apex Tests', async () => { + let prompt: InputBox | QuickOpenBox; + let testSetup: TestSetup; + const testReqConfig: utilities.TestReqConfig = { + projectConfig: { + projectShape: utilities.ProjectShapeOption.NEW + }, + isOrgRequired: true, + testSuiteSuffixName: 'RunApexTests' + }; + + step('Set up the testing environment', async () => { + utilities.log(`RunApexTests - Set up the testing environment`); + testSetup = await TestSetup.setUp(testReqConfig); + + // Create Apex class 1 and test + try { + await utilities.createApexClassWithTest('ExampleApexClass1'); + } catch (error) { + await utilities.createApexClassWithTest('ExampleApexClass1'); + } + + // Create Apex class 2 and test + try { + await utilities.createApexClassWithTest('ExampleApexClass2'); + } catch (error) { + await utilities.createApexClassWithTest('ExampleApexClass2'); + } + + // Create Apex class 3 and test + try { + await utilities.createApexClassWithTest('ExampleApexClass3'); + } catch (error) { + await utilities.createApexClassWithTest('ExampleApexClass3'); + } + + // Push source to org + await utilities.executeQuickPick( + 'SFDX: Push Source to Default Org and Ignore Conflicts', + utilities.Duration.seconds(1) + ); + + // Look for the success notification that appears which says, "SFDX: Push Source to Default Org and Ignore Conflicts successfully ran". + let successPushNotificationWasFound; + try { + successPushNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Push Source to Default Org and Ignore Conflicts successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successPushNotificationWasFound).to.equal(true); + } catch (error) { + await utilities.getWorkbench().openNotificationsCenter(); + successPushNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Push Source to Default Org and Ignore Conflicts successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successPushNotificationWasFound).to.equal(true); + } + }); + + step('Verify LSP finished indexing', async () => { + utilities.log(`${testSetup.testSuiteSuffixName} - Verify LSP finished indexing`); + + // Get Apex LSP Status Bar + const statusBar = await utilities.getStatusBarItemWhichIncludes('Editor Language Status'); + await statusBar.click(); + expect(await statusBar.getAttribute('aria-label')).to.contain('Indexing complete'); + }); + + step('Run All Tests via Apex Class', async () => { + utilities.log(`RunApexTests - Run All Tests via Apex Class`); + const workbench = utilities.getWorkbench(); + const textEditor = await utilities.getTextEditor(workbench, 'ExampleApexClass1Test.cls'); + + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Click the "Run All Tests" code lens at the top of the class + const runAllTestsOption = await textEditor.getCodeLens('Run All Tests'); + await runAllTestsOption!.click(); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await workbench.openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that all tests pass + const outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 1', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass1Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Run Single Test via Apex Class', async () => { + utilities.log(`RunApexTests - Run Single Test via Apex Class`); + const workbench = utilities.getWorkbench(); + const textEditor = await utilities.getTextEditor(workbench, 'ExampleApexClass2Test.cls'); + + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Click the "Run Test" code lens at the top of one of the test methods + const runTestOption = await textEditor.getCodeLens('Run Test'); + await runTestOption!.click(); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await workbench.openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that all tests pass + const outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 1', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass2Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Run All Tests via Command Palette', async () => { + utilities.log(`RunApexTests - Run All Tests via Command Palette`); + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Run SFDX: Run Apex tests. + prompt = await utilities.executeQuickPick('SFDX: Run Apex Tests', utilities.Duration.seconds(1)); + + // Select the "All Tests" option + await prompt.selectQuickPick('All Tests'); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await utilities.getWorkbench().openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that all tests pass + const outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 3', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass1Test.validateSayHello Pass', + 'ExampleApexClass2Test.validateSayHello Pass', + 'ExampleApexClass3Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Run Single Class via Command Palette', async () => { + utilities.log(`RunApexTests - Run Single Class via Command Palette`); + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Run SFDX: Run Apex tests. + prompt = await utilities.executeQuickPick('SFDX: Run Apex Tests', utilities.Duration.seconds(1)); + + // Select the "ExampleApexClass1Test" file + await prompt.selectQuickPick('ExampleApexClass1Test'); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await utilities.getWorkbench().openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that all tests pass + const outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 1', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass1Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Run All tests via Test Sidebar', async () => { + utilities.log(`RunApexTests - Run All tests via Test Sidebar`); + const workbench = utilities.getWorkbench(); + const testingView = await workbench.getActivityBar().getViewControl('Testing'); + expect(testingView).to.not.be.undefined; + // Open the Test Sidebar + const testingSideBarView = await testingView?.openView(); + expect(testingSideBarView).to.be.instanceOf(SideBarView); + + const apexTestsSection = await utilities.getTestsSection(workbench, 'Apex Tests'); + const expectedItems = ['ExampleApexClass1Test', 'ExampleApexClass2Test', 'ExampleApexClass3Test']; + const apexTestsItems = await utilities.verifyTestItemsInSideBar( + apexTestsSection, + 'Refresh Tests', + expectedItems, + 6, + 3 + ); + + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Click the run tests button on the top right corner of the Test sidebar + await apexTestsSection.click(); + const runTestsAction = await apexTestsSection.getAction('Run Tests'); + await runTestsAction!.click(); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await workbench.openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that all tests pass + const outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 3', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass1Test.validateSayHello Pass', + 'ExampleApexClass2Test.validateSayHello Pass', + 'ExampleApexClass3Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + + // Verify the tests that are passing are labeled with a green dot on the Test sidebar + for (const item of apexTestsItems) { + await utilities.verifyTestIconColor(item, 'testPass'); + } + }); + + step('Run All Tests on a Class via the Test Sidebar', async () => { + utilities.log(`RunApexTests - Run All Tests on a Class via the Test Sidebar`); + const workbench = utilities.getWorkbench(); + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + const outputPanelText = await utilities.runTestCaseFromSideBar( + workbench, + 'Apex Tests', + 'ExampleApexClass2Test', + 'Run Tests' + ); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 1', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass2Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Run Single Test via the Test Sidebar', async () => { + utilities.log(`RunApexTests - 'Run Single Test via the Test Sidebar`); + const workbench = utilities.getWorkbench(); + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + const outputPanelText = await utilities.runTestCaseFromSideBar( + workbench, + 'Apex Tests', + 'validateSayHello', + 'Run Single Test' + ); + const expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 1', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass1Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Run a test that fails and fix it', async () => { + utilities.log(`RunApexTests - Run a test that fails and fix it`); + // Create Apex class AccountService + await utilities.createApexClassWithBugs(); + + // Push source to org + const workbench = utilities.getWorkbench(); + await utilities.executeQuickPick( + 'SFDX: Push Source to Default Org and Ignore Conflicts', + utilities.Duration.seconds(1) + ); + + // Look for the success notification that appears which says, "SFDX: Push Source to Default Org and Ignore Conflicts successfully ran". + let successPushNotificationWasFound; + try { + successPushNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Push Source to Default Org and Ignore Conflicts successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successPushNotificationWasFound).to.equal(true); + } catch (error) { + await utilities.getWorkbench().openNotificationsCenter(); + successPushNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Push Source to Default Org and Ignore Conflicts successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successPushNotificationWasFound).to.equal(true); + } + + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Run SFDX: Run Apex tests. + prompt = await utilities.executeQuickPick('SFDX: Run Apex Tests', utilities.Duration.seconds(1)); + + // Select the "AccountServiceTest" file + await prompt.setText('AccountServiceTest'); + await prompt.confirm(); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await workbench.openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that the test fails + let outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + let expectedTexts = ['Assertion Failed: incorrect ticker symbol', 'Expected: CRM, Actual: SFDC']; + + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + + // Fix test + const textEditor = await utilities.getTextEditor(workbench, 'AccountService.cls'); + await textEditor.setTextAtLine(6, '\t\t\tTickerSymbol = tickerSymbol'); + await textEditor.save(); + await utilities.pause(utilities.Duration.seconds(1)); + + // Push source to org + await utilities.executeQuickPick( + 'SFDX: Push Source to Default Org and Ignore Conflicts', + utilities.Duration.seconds(1) + ); + + // Look for the success notification that appears which says, "SFDX: Push Source to Default Org and Ignore Conflicts successfully ran". + let successPushNotification2WasFound; + try { + successPushNotification2WasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Push Source to Default Org and Ignore Conflicts successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successPushNotification2WasFound).to.equal(true); + } catch (error) { + await utilities.getWorkbench().openNotificationsCenter(); + successPushNotification2WasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Push Source to Default Org and Ignore Conflicts successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successPushNotification2WasFound).to.equal(true); + } + + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Run SFDX: Run Apex tests to verify fix + prompt = await utilities.executeQuickPick('SFDX: Run Apex Tests', utilities.Duration.seconds(1)); + + // Select the "AccountServiceTest" file + await prompt.setText('AccountServiceTest'); + await prompt.confirm(); + + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + const successNotification2WasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotification2WasFound).to.equal(true); + + // Verify test results are listed on vscode's Output section + outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + expectedTexts = [ + '=== Test Summary', + 'Outcome Passed', + 'Tests Ran 1', + 'Pass Rate 100%', + 'TEST NAME', + 'AccountServiceTest.should_create_account Pass', + 'ended SFDX: Run Apex Tests' + ]; + + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + step('Create Apex Test Suite', async () => { + utilities.log(`RunApexTests - Create Apex Test Suite`); + // Run SFDX: Create Apex Test Suite. + prompt = await utilities.executeQuickPick('SFDX: Create Apex Test Suite', utilities.Duration.seconds(2)); + + // Set the name of the new Apex Test Suite + await prompt.setText('ApexTestSuite'); + await prompt.confirm(); + await utilities.pause(utilities.Duration.seconds(2)); + + // Choose tests that will belong to the new Apex Test Suite + await prompt.setText('ExampleApexClass1Test'); + const checkbox = await prompt.findElement(By.css('input.quick-input-list-checkbox')); + await checkbox.click(); + await utilities.clickFilePathOkButton(); + + // Look for the success notification that appears which says, "SFDX: Build Apex Test Suite successfully ran". + const successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Build Apex Test Suite successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + }); + + step('Add test to Apex Test Suite', async () => { + utilities.log(`RunApexTests - Add test to Apex Test Suite`); + // Run SFDX: Add Tests to Apex Test Suite. + prompt = await utilities.executeQuickPick('SFDX: Add Tests to Apex Test Suite', utilities.Duration.seconds(1)); + + // Select the suite recently created called ApexTestSuite + await prompt.selectQuickPick('ApexTestSuite'); + await utilities.pause(utilities.Duration.seconds(2)); + + // Choose tests that will belong to the already created Apex Test Suite + await prompt.setText('ExampleApexClass2Test'); + const checkbox = await prompt.findElement(By.css('input.quick-input-list-checkbox')); + await checkbox.click(); + await utilities.clickFilePathOkButton(); + + // Look for the success notification that appears which says, "SFDX: Build Apex Test Suite successfully ran". + const successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Build Apex Test Suite successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + }); + + step('Run Apex Test Suite', async () => { + utilities.log(`RunApexTests - Run Apex Test Suite`); + // Clear the Output view. + await utilities.dismissAllNotifications(); + await utilities.clearOutputView(utilities.Duration.seconds(2)); + + // Run SFDX: Run Apex Test Suite. + await utilities.executeQuickPick('SFDX: Run Apex Test Suite', utilities.Duration.seconds(1)); + + // Select the suite recently created called ApexTestSuite + await prompt.selectQuickPick('ApexTestSuite'); + // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". + let successNotificationWasFound; + try { + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await utilities.getWorkbench().openNotificationsCenter(); + successNotificationWasFound = await utilities.notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + utilities.Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } + + // Verify test results are listed on vscode's Output section + // Also verify that all tests pass + const outputPanelText = await utilities.attemptToFindOutputPanelText('Apex', '=== Test Results', 10); + const expectedTexts = [ + '=== Test Summary', + 'TEST NAME', + 'ended SFDX: Run Apex Tests', + 'Outcome Passed', + 'Tests Ran 2', + 'Pass Rate 100%', + 'TEST NAME', + 'ExampleApexClass1Test.validateSayHello Pass', + 'ExampleApexClass2Test.validateSayHello Pass', + 'ended SFDX: Run Apex Tests' + ]; + expect(outputPanelText).to.not.be.undefined; + await utilities.verifyOutputPanelText(outputPanelText!, expectedTexts); + }); + + after('Tear down and clean up the testing environment', async () => { + utilities.log(`RunApexTests - Tear down and clean up the testing environment`); + await testSetup?.tearDown(); + }); +}); diff --git a/test/testSetup.ts b/test/testSetup.ts index e5510e6..b90aa6c 100644 --- a/test/testSetup.ts +++ b/test/testSetup.ts @@ -40,6 +40,7 @@ export class TestSetup { await utilities.setUpScratchOrg(testSetup, scratchOrgEdition); await utilities.reloadAndEnableExtensions(); // This is necessary in order to update JAVA home path } + testSetup.setWorkbenchHoverDelay(); utilities.log(`${testSetup.testSuiteSuffixName} - ...finished TestSetup.setUp()`); return testSetup; } @@ -50,9 +51,7 @@ export class TestSetup { await utilities.deleteScratchOrg(this.scratchOrgAliasName); await utilities.deleteScratchOrgInfo(this); } catch (error) { - utilities.log( - `Deleting scratch org (or info) failed with Error: ${(error as Error).message}` - ); + utilities.log(`Deleting scratch org (or info) failed with Error: ${(error as Error).message}`); } } @@ -77,15 +76,11 @@ export class TestSetup { // verify if folder matches the github repo url const repoExists = await utilities.gitRepoExists(projectConfig.githubRepoUrl); if (!repoExists) { - this.throwError( - `Repository does not exist or is inaccessible: ${projectConfig.githubRepoUrl}` - ); + this.throwError(`Repository does not exist or is inaccessible: ${projectConfig.githubRepoUrl}`); } const repoName = utilities.getRepoNameFromUrl(projectConfig.githubRepoUrl); if (!repoName) { - this.throwError( - `Unable to determine repository name from URL: ${projectConfig.githubRepoUrl}` - ); + this.throwError(`Unable to determine repository name from URL: ${projectConfig.githubRepoUrl}`); } else { projectName = repoName; if (projectConfig.folderPath) { @@ -148,16 +143,9 @@ export class TestSetup { public updateScratchOrgDefWithEdition(scratchOrgEdition: utilities.OrgEdition) { if (scratchOrgEdition === 'enterprise') { - const projectScratchDefPath = path.join( - this.projectFolderPath!, - 'config', - 'project-scratch-def.json' - ); + const projectScratchDefPath = path.join(this.projectFolderPath!, 'config', 'project-scratch-def.json'); let projectScratchDef = fs.readFileSync(projectScratchDefPath, 'utf8'); - projectScratchDef = projectScratchDef.replace( - `"edition": "Developer"`, - `"edition": "Enterprise"` - ); + projectScratchDef = projectScratchDef.replace(`"edition": "Developer"`, `"edition": "Enterprise"`); fs.writeFileSync(projectScratchDefPath, projectScratchDef, 'utf8'); } } @@ -171,19 +159,33 @@ export class TestSetup { fs.mkdirSync(path.dirname(vscodeSettingsPath), { recursive: true }); } - let settings = fs.existsSync(vscodeSettingsPath) - ? JSON.parse(fs.readFileSync(vscodeSettingsPath, 'utf8')) - : {}; + let settings = fs.existsSync(vscodeSettingsPath) ? JSON.parse(fs.readFileSync(vscodeSettingsPath, 'utf8')) : {}; settings = { ...settings, - ...(process.env.JAVA_HOME - ? { 'salesforcedx-vscode-apex.java.home': process.env.JAVA_HOME } - : {}) + ...(process.env.JAVA_HOME ? { 'salesforcedx-vscode-apex.java.home': process.env.JAVA_HOME } : {}) }; fs.writeFileSync(vscodeSettingsPath, JSON.stringify(settings, null, 2), 'utf8'); utilities.log( `${this.testSuiteSuffixName} - Set 'salesforcedx-vscode-apex.java.home' to '${process.env.JAVA_HOME}' in ${vscodeSettingsPath}` ); } + private setWorkbenchHoverDelay(): void { + const vscodeSettingsPath = path.join(this.projectFolderPath!, '.vscode', 'settings.json'); + + if (!fs.existsSync(path.dirname(vscodeSettingsPath))) { + fs.mkdirSync(path.dirname(vscodeSettingsPath), { recursive: true }); + } + + let settings = fs.existsSync(vscodeSettingsPath) ? JSON.parse(fs.readFileSync(vscodeSettingsPath, 'utf8')) : {}; + + // Update settings to set workbench.hover.delay + settings = { + ...settings, + 'workbench.hover.delay': 300000 + }; + + fs.writeFileSync(vscodeSettingsPath, JSON.stringify(settings, null, 2), 'utf8'); + utilities.log(`${this.testSuiteSuffixName} - Set 'workbench.hover.delay' to '300000' in ${vscodeSettingsPath}`); + } } diff --git a/test/utilities/apexUtils.ts b/test/utilities/apexUtils.ts index 240aa7e..e75ad54 100644 --- a/test/utilities/apexUtils.ts +++ b/test/utilities/apexUtils.ts @@ -11,6 +11,7 @@ import { getWorkbench } from './workbench'; import { getTextEditor } from './textEditorView'; export async function createApexClass(name: string, classText: string, breakpoint?: number): Promise { + log(`calling createApexClass(${name})`); // Using the Command palette, run SFDX: Create Apex Class to create the main class const inputBox = await executeQuickPick('SFDX: Create Apex Class', Duration.seconds(2)); @@ -32,6 +33,7 @@ export async function createApexClass(name: string, classText: string, breakpoin } export async function createApexClassWithTest(name: string): Promise { + log(`callig createApexClassWithTest()`); const classText = [ `public with sharing class ${name} {`, `\tpublic static void SayHello(string name){`, @@ -57,6 +59,7 @@ export async function createApexClassWithTest(name: string): Promise { } export async function createApexClassWithBugs(): Promise { + log(`calling createApexClassWithBugs()`); const classText = [ `public with sharing class AccountService {`, `\tpublic Account createAccount(String accountName, String accountNumber, String tickerSymbol) {`, @@ -96,6 +99,7 @@ export async function createApexClassWithBugs(): Promise { } export async function createAnonymousApexFile(): Promise { + log(`calling createAnonymousApexFile()`); const workbench = getWorkbench(); // Using the Command palette, run File: New File... diff --git a/test/utilities/notifications.ts b/test/utilities/notifications.ts index 002d49e..d888cb1 100644 --- a/test/utilities/notifications.ts +++ b/test/utilities/notifications.ts @@ -74,6 +74,7 @@ export async function acceptNotification( } export async function dismissAllNotifications(): Promise { + log(`calling dismissAllNotifications()`); await executeQuickPick('Notifications: Clear All Notifications'); } @@ -84,32 +85,42 @@ async function findNotification( throwOnTimeout: boolean = false // New parameter to control throwing on timeout ): Promise { const workbench = getWorkbench(); + const timeoutMessage = `Notification with message "${message}" ${shouldBePresent ? 'not found' : 'still present'} within the specified timeout of ${timeout.seconds} seconds.`; + + const getMatchingNotification = async (): Promise => { + await workbench.openNotificationsCenter(); + const notifications = await workbench.getNotifications(); + for (const notification of notifications) { + const notificationMessage = await notification.getMessage(); + if (notificationMessage === message || notificationMessage.includes(message)) { + return notification as unknown as Notification; + } + } + return null; + }; try { - const foundNotification = await getBrowser().wait( - async () => { - const notifications = await workbench.getNotifications(); - let bestMatch: Notification | null = null; - - for (const notification of notifications) { - const notificationMessage = await notification.getMessage(); - if (notificationMessage === message || notificationMessage.startsWith(message)) { - bestMatch = notification as unknown as Notification; - break; - } - } - return bestMatch; - }, - timeout.milliseconds, - `Notification with message "${message}" ${shouldBePresent ? 'not found' : 'still present'} within the specified timeout of ${timeout.seconds} seconds.` - ); - - return shouldBePresent ? foundNotification : null; + const endTime = Date.now() + timeout.milliseconds; + let foundNotification: Notification | null = null; + + // Retry until timeout is reached or the notification status matches `shouldBePresent` + do { + foundNotification = await getMatchingNotification(); + if (foundNotification) { + return foundNotification; + } + await new Promise(res => setTimeout(res, 100)); // Short delay before retrying + } while (Date.now() < endTime); + + // Throw or return based on `throwOnTimeout` + if (throwOnTimeout) { + throw new Error(timeoutMessage); + } + return null; } catch (error) { if (throwOnTimeout) { - throw error; // Re-throw the error if throwOnTimeout is true + throw error; } - // Handle the timeout gracefully return null; } } diff --git a/test/utilities/outputView.ts b/test/utilities/outputView.ts index 6ce938e..41c1a30 100644 --- a/test/utilities/outputView.ts +++ b/test/utilities/outputView.ts @@ -112,6 +112,7 @@ export async function getOperationTime(outputText: string): Promise { } export async function clearOutputView(wait = Duration.seconds(1)) { + log(`calling clearOutputView()`); await executeQuickPick('View: Clear Output', wait); } diff --git a/test/utilities/testUtils.ts b/test/utilities/testUtils.ts index 745c3ee..2bb4957 100644 --- a/test/utilities/testUtils.ts +++ b/test/utilities/testUtils.ts @@ -44,14 +44,14 @@ export async function getTestsSection(workbench: Workbench, type: string) { * Runs a test case from the sidebar and returns the test result. * * * @param {Workbench} workbench - The workbench instance used to interact with the sidebar and views. - * @param {string} testSuite - The name of the test suite from which to run the test (e.g., 'APEX Tests', 'LWC Tests'). + * @param {string} testSuite - The name of the test suite from which to run the test (e.g., 'Apex Tests', 'LWC Tests'). * @param {string} testName - The name of the specific test case to run. * @param {string} actionLabel - The label of the action button to click (e.g., 'SFDX: Run Lightning Web Component Test File', 'Run Single Test'). * * @example * const result = await runTestCaseFromSideBar( * myWorkbench, - * 'APEX Tests', + * 'Apex Tests', * 'MyApexTestCase', * 'Run Single Test' * ); @@ -83,13 +83,23 @@ export async function runTestCaseFromSideBar( await actionButton?.click(); let testResult: string | undefined; - if (testSuite === 'APEX Tests') { + if (testSuite === 'Apex Tests') { // Look for the success notification that appears which says, "SFDX: Run Apex Tests successfully ran". - const successNotificationWasFound = await notificationIsPresentWithTimeout( - 'SFDX: Run Apex Tests successfully ran', - Duration.TEN_MINUTES - ); - expect(successNotificationWasFound).to.be.true; + let successNotificationWasFound; + try { + successNotificationWasFound = await notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + Duration.TEN_MINUTES + ); + expect(successNotificationWasFound).to.equal(true); + } catch (error) { + await workbench.openNotificationsCenter(); + successNotificationWasFound = await notificationIsPresentWithTimeout( + 'SFDX: Run Apex Tests successfully ran', + Duration.ONE_MINUTE + ); + expect(successNotificationWasFound).to.equal(true); + } testResult = await attemptToFindOutputPanelText('Apex', '=== Test Results', 10); } else if (testSuite === 'LWC Tests') { testResult = await getTerminalViewText(workbench, 15); diff --git a/test/utilities/textEditorView.ts b/test/utilities/textEditorView.ts index 67e7f22..4018ef6 100644 --- a/test/utilities/textEditorView.ts +++ b/test/utilities/textEditorView.ts @@ -1,6 +1,6 @@ import { TextEditor, Workbench } from 'vscode-extension-tester'; import { executeQuickPick } from './commandPrompt'; -import { Duration, pause } from './miscellaneous'; +import { Duration, log, pause } from './miscellaneous'; /** * @param workbench page object representing the custom VSCode title bar @@ -8,6 +8,7 @@ import { Duration, pause } from './miscellaneous'; * @returns editor for the given file name */ export async function getTextEditor(workbench: Workbench, fileName: string): Promise { + log(`calling getTextEditor(${fileName})`); const inputBox = await executeQuickPick('Go to File...', Duration.seconds(1)); await inputBox.setText(fileName); await inputBox.confirm();