diff --git a/packages/salesforcedx-utils-vscode/src/context/workspaceContextUtil.ts b/packages/salesforcedx-utils-vscode/src/context/workspaceContextUtil.ts index 3176ce2c77..a465eb8251 100644 --- a/packages/salesforcedx-utils-vscode/src/context/workspaceContextUtil.ts +++ b/packages/salesforcedx-utils-vscode/src/context/workspaceContextUtil.ts @@ -100,7 +100,7 @@ export class WorkspaceContextUtil { this._orgId = ''; if (error instanceof Error) { console.log( - 'There was an problem getting the orgId of the default org: ', + 'There was a problem getting the orgId of the default org: ', error ); TelemetryService.getInstance().sendException( diff --git a/packages/salesforcedx-vscode-core/src/orgPicker/orgList.ts b/packages/salesforcedx-vscode-core/src/orgPicker/orgList.ts index 7e730ebc06..e5a9d2ee68 100644 --- a/packages/salesforcedx-vscode-core/src/orgPicker/orgList.ts +++ b/packages/salesforcedx-vscode-core/src/orgPicker/orgList.ts @@ -4,10 +4,13 @@ * 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 { AuthFields, AuthInfo, OrgAuthorization } from '@salesforce/core-bundle'; +import { + AuthFields, + AuthInfo, + OrgAuthorization +} from '@salesforce/core-bundle'; import { CancelResponse, - ConfigUtil, ContinueResponse, OrgUserInfo } from '@salesforce/salesforcedx-utils-vscode'; @@ -61,7 +64,6 @@ export class OrgList implements vscode.Disposable { const targetDevHub = await OrgAuthInfo.getDevHubUsername(); const authList = []; - const today = new Date(); for (const orgAuth of orgAuthorizations) { // When this is called right after logging out of an org, there can // still be a cached Org Auth in the list with a "No auth information found" @@ -88,22 +90,16 @@ export class OrgList implements vscode.Disposable { // scratch orgs parented by other (non-default) devHub orgs continue; } - const isExpired = - authFields && authFields.expirationDate - ? today >= new Date(authFields.expirationDate) - : false; - - const aliases = await ConfigUtil.getAllAliasesFor(orgAuth.username); - let authListItem = - aliases && aliases.length > 0 - ? `${aliases.join(',')} - ${orgAuth.username}` - : orgAuth.username; - if (isExpired) { - authListItem += ` - ${nls.localize( - 'org_expired' - )} ${String.fromCodePoint(0x274c)}`; // cross-mark + if (orgAuth.isExpired === true) { + // If the scratch org is expired we don't want to see it in the org picker + continue; } + const aliases = orgAuth.aliases || []; + const authListItem = + aliases.length > 0 + ? `${aliases.join(',')} - ${orgAuth.username}` + : orgAuth.username; authList.push(authListItem); } @@ -111,11 +107,9 @@ export class OrgList implements vscode.Disposable { } public async updateOrgList(): Promise { - const orgAuthorizations = await this.getOrgAuthorizations(); - if (orgAuthorizations && orgAuthorizations.length === 0) { - return []; - } - const authUsernameList = await this.filterAuthInfo(orgAuthorizations); + const authUsernameList = await this.filterAuthInfo( + await this.getOrgAuthorizations() + ); return authUsernameList; } diff --git a/packages/salesforcedx-vscode-core/test/jest/orgPicker/orgPicker.test.ts b/packages/salesforcedx-vscode-core/test/jest/orgPicker/orgPicker.test.ts new file mode 100644 index 0000000000..4f652cdc86 --- /dev/null +++ b/packages/salesforcedx-vscode-core/test/jest/orgPicker/orgPicker.test.ts @@ -0,0 +1,144 @@ +/* + * 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 { AuthFields, OrgAuthorization } from '@salesforce/core-bundle'; +import * as vscode from 'vscode'; +import { OrgList } from '../../../src/orgPicker'; +import { OrgAuthInfo } from '../../../src/util'; + +describe('OrgList - filterAuthInfo', () => { + let orgList: OrgList; + let createFileSystemWatcherMock: jest.SpyInstance; + let getDevHubUsernameMock: jest.SpyInstance; + let getAuthFieldsForMock: jest.SpyInstance; + let mockWatcher: any; + + const dummyDevHubUsername = 'test-devhub@example.com'; + + const createOrgAuthorization = ( + overrides: Partial = {} + ): OrgAuthorization => ({ + orgId: '000', + username: 'test-username@example.com', + oauthMethod: 'unknown', + aliases: [], + configs: [], + isScratchOrg: undefined, + isDevHub: undefined, + isSandbox: undefined, + instanceUrl: undefined, + accessToken: undefined, + error: undefined, + isExpired: false, + ...overrides + }); + + beforeEach(() => { + mockWatcher = { + onDidChange: jest.fn(), + onDidCreate: jest.fn(), + onDidDelete: jest.fn() + }; + createFileSystemWatcherMock = ( + vscode.workspace.createFileSystemWatcher as any + ).mockReturnValue(mockWatcher); + (vscode.window.createStatusBarItem as jest.Mock).mockReturnValue({ + command: '', + text: '', + tooltip: '', + show: jest.fn(), + dispose: jest.fn() + }); + orgList = new OrgList(); + getAuthFieldsForMock = jest.spyOn(OrgList.prototype, 'getAuthFieldsFor'); + getDevHubUsernameMock = jest.spyOn(OrgAuthInfo, 'getDevHubUsername'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should filter out orgs with the scratchAdminUsername field', async () => { + const orgAuth = createOrgAuthorization(); + const orgAuths = [orgAuth]; + getAuthFieldsForMock.mockResolvedValueOnce({ + scratchAdminUsername: 'admin@example.com' + } as AuthFields); + getDevHubUsernameMock.mockResolvedValueOnce(null); + + const result = await orgList.filterAuthInfo(orgAuths); + + expect(result).toEqual([]); + }); + + it('should filter out scratch orgs parented by non-default Dev Hubs', async () => { + const orgAuth = createOrgAuthorization({ isScratchOrg: true }); + const orgAuths = [orgAuth]; + getAuthFieldsForMock.mockResolvedValueOnce({ + devHubUsername: 'other-devhub@example.com' + } as AuthFields); + getDevHubUsernameMock.mockResolvedValueOnce(dummyDevHubUsername); + + const result = await orgList.filterAuthInfo(orgAuths); + + expect(result).toEqual([]); + }); + + it('should filter out expired orgs', async () => { + const expiredOrgAuth = createOrgAuthorization({ + username: 'expired-org@example.com', + isExpired: true + }); + const orgAuths = [expiredOrgAuth]; + getAuthFieldsForMock.mockResolvedValueOnce({} as AuthFields); + getDevHubUsernameMock.mockResolvedValueOnce(dummyDevHubUsername); + + const result = await orgList.filterAuthInfo(orgAuths); + + expect(result).toEqual([]); + }); + + it('should include aliases in the result if available', async () => { + const orgAuth = createOrgAuthorization({ + username: 'test-username@example.com', + aliases: ['alias1'] + }); + const orgAuths = [orgAuth]; + getAuthFieldsForMock.mockResolvedValueOnce({} as AuthFields); + getDevHubUsernameMock.mockResolvedValueOnce(null); + + const result = await orgList.filterAuthInfo(orgAuths); + + expect(result).toEqual(['alias1 - test-username@example.com']); + }); + + it('should filter out org authorizations with errors', async () => { + const orgAuthWithError = createOrgAuthorization({ + username: 'error-org@example.com', + error: 'Some error' + }); + const orgAuths = [orgAuthWithError]; + getAuthFieldsForMock.mockResolvedValueOnce({} as AuthFields); + getDevHubUsernameMock.mockResolvedValueOnce(null); + + const result = await orgList.filterAuthInfo(orgAuths); + + expect(result).toEqual([]); + }); + + it('should return a list of valid org authorizations', async () => { + const validOrgAuth = createOrgAuthorization({ + username: 'valid-org@example.com' + }); + const orgAuths = [validOrgAuth]; + getAuthFieldsForMock.mockResolvedValueOnce({} as AuthFields); + getDevHubUsernameMock.mockResolvedValueOnce(dummyDevHubUsername); + + const result = await orgList.filterAuthInfo(orgAuths); + + expect(result).toEqual(['valid-org@example.com']); + }); +}); diff --git a/packages/salesforcedx-vscode-core/test/vscode-integration/orgPicker/orgList.test.ts b/packages/salesforcedx-vscode-core/test/vscode-integration/orgPicker/orgList.test.ts index 73d75a357a..fa7ed98811 100644 --- a/packages/salesforcedx-vscode-core/test/vscode-integration/orgPicker/orgList.test.ts +++ b/packages/salesforcedx-vscode-core/test/vscode-integration/orgPicker/orgList.test.ts @@ -4,8 +4,11 @@ * 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 { AuthInfo, OrgAuthorization, StateAggregator } from '@salesforce/core-bundle'; -import { ConfigUtil } from '@salesforce/salesforcedx-utils-vscode'; +import { + AuthInfo, + OrgAuthorization, + StateAggregator +} from '@salesforce/core-bundle'; import { expect } from 'chai'; import { createSandbox, SinonStub } from 'sinon'; import * as vscode from 'vscode'; @@ -14,7 +17,6 @@ import { OrgList } from '../../../src/orgPicker'; import * as util from '../../../src/util'; import { OrgAuthInfo } from '../../../src/util'; -const AN_ALIAS = 'anAlias'; const sandbox = createSandbox(); describe('orgList Tests', () => { @@ -35,8 +37,8 @@ describe('orgList Tests', () => { it('should return a list of FileInfo objects when given an array of file names', async () => { // Arrange const authFilesArray = [ - { username: 'test-username1@example.com' }, - { username: 'test-username2@example.com' } + { username: 'test-username1@example.com', aliases: ['alias1'] }, + { username: 'test-username2@example.com', aliases: ['alias2'] } ]; getAuthInfoListAllAuthorizationsStub.resolves(authFilesArray); const orgList = new OrgList(); @@ -67,7 +69,6 @@ describe('orgList Tests', () => { let getUsernameStub: SinonStub; let stateAggregatorCreateStub: SinonStub; let getAllStub: SinonStub; - let getAllAliasesForStub: SinonStub; let getAuthFieldsForStub: SinonStub; const orgList = new OrgList(); @@ -87,7 +88,6 @@ describe('orgList Tests', () => { .stub(StateAggregator, 'create') .resolves(fakeStateAggregator); - getAllAliasesForStub = sandbox.stub(ConfigUtil, 'getAllAliasesFor'); getAuthFieldsForStub = sandbox.stub( OrgList.prototype, 'getAuthFieldsFor' @@ -116,7 +116,8 @@ describe('orgList Tests', () => { const dummyOrgAuth1 = getFakeOrgAuthorization({ orgId: '000', - username: 'test-username1@example.com' + username: 'test-username1@example.com', + aliases: ['alias1'] }); const dummyOrgAuth2 = getFakeOrgAuthorization({ orgId: '111', @@ -129,13 +130,15 @@ describe('orgList Tests', () => { }); const dummyScratchOrgAuth2 = getFakeOrgAuthorization({ orgId: '111', - username: 'test-scratchorg2@example.com' + username: 'test-scratchorg2@example.com', + aliases: ['anAlias'] }); const dummyScratchOrgAuthWithError = getFakeOrgAuthorization({ orgId: '222', username: 'test-scratchorg3@example.com', error: - 'No authorization information found for test-scratchorg3@example.com.' + 'No authorization information found for test-scratchorg3@example.com.', + aliases: ['anAlias'] }); const dummyDevHubUsername1 = 'test-devhub1@example.com'; @@ -209,9 +212,6 @@ describe('orgList Tests', () => { .returns({ devHubUsername: dummyDevHubUsername1 }); - getAllAliasesForStub - .withArgs(authInfoObjectsWithOneError[0].username) - .returns([AN_ALIAS]); getDevHubUsernameStub.resolves(dummyDevHubUsername1); const authList = await orgList.filterAuthInfo( @@ -219,7 +219,6 @@ describe('orgList Tests', () => { ); expect(getDevHubUsernameStub.calledOnce).to.equal(true); expect(authList.length).to.equal(1); - expect(authList[0].includes(AN_ALIAS)).to.equal(true); expect(authList[0].includes(dummyScratchOrgAuth1.username)).to.equal( true ); @@ -233,9 +232,6 @@ describe('orgList Tests', () => { dummyOrgAuth2 ]; getAllStub.withArgs(dummyOrgAuth1.username).returns(['alias1']); - getAllAliasesForStub - .withArgs(dummyOrgAuth1.username) - .returns(['alias1']); getAuthFieldsForStub.withArgs(authInfoObjects[0].username).returns({}); // Act @@ -245,7 +241,7 @@ describe('orgList Tests', () => { expect(authList[0]).to.equal('alias1 - test-username1@example.com'); }); - it('should flag the org as expired if expiration date has passed', async () => { + it('should filter the list to ignore expired orgs', async () => { const oneDayMillis = 60 * 60 * 24 * 1000; const today = new Date(); const yesterday = new Date(today.getTime() - oneDayMillis); @@ -254,15 +250,18 @@ describe('orgList Tests', () => { const authInfoObjects: OrgAuthorization[] = [ getFakeOrgAuthorization({ orgId: '000', - username: 'test-scratchorg-today@example.com' + username: 'test-scratchorg-today@example.com', + isExpired: true }), getFakeOrgAuthorization({ orgId: '111', - username: 'test-scratchorg-yesterday@example.com' + username: 'test-scratchorg-yesterday@example.com', + isExpired: true }), getFakeOrgAuthorization({ orgId: '222', - username: 'test-scratchorg-tomorrow@example.com' + username: 'test-scratchorg-tomorrow@example.com', + isExpired: false }) ]; @@ -290,19 +289,11 @@ describe('orgList Tests', () => { const authList = await orgList.filterAuthInfo(authInfoObjects); - expect(authList[0]).to.equal( - 'test-scratchorg-today@example.com - ' + - nls.localize('org_expired') + - ' ' + - String.fromCodePoint(0x274c) - ); - expect(authList[1]).to.equal( - 'test-scratchorg-yesterday@example.com - ' + - nls.localize('org_expired') + - ' ' + - String.fromCodePoint(0x274c) + expect(authList).to.not.contain('test-scratchorg-today@example.com'); + expect(authList).to.not.contain( + 'test-scratchorg-yesterday@example.com' ); - expect(authList[2]).to.equal('test-scratchorg-tomorrow@example.com'); + expect(authList[0]).to.equal('test-scratchorg-tomorrow@example.com'); }); }); describe('Set Default Org', () => {