From 6f5bb9b8f8190135506815ddb958ee453d685216 Mon Sep 17 00:00:00 2001 From: Nicolas Kruk Date: Sun, 12 Oct 2025 12:04:32 -0700 Subject: [PATCH 1/4] fix: strip trailing slashes from instance URL in LightningDevComponent --- src/commands/lightning/dev/component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/commands/lightning/dev/component.ts b/src/commands/lightning/dev/component.ts index 0b40dd3..fcd8cb1 100644 --- a/src/commands/lightning/dev/component.ts +++ b/src/commands/lightning/dev/component.ts @@ -154,8 +154,11 @@ export default class LightningDevComponent extends SfCommand Date: Sun, 12 Oct 2025 19:08:47 +0000 Subject: [PATCH 2/4] chore(release): 4.5.2-alpha.0 [skip ci] --- README.md | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4627ff8..2e832c1 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ EXAMPLES $ sf lightning dev app --target-org myOrg --device-type ios --device-id "iPhone 15 Pro Max" ``` -_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.1/src/commands/lightning/dev/app.ts)_ +_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.2-alpha.0/src/commands/lightning/dev/app.ts)_ ## `sf lightning dev component` @@ -249,7 +249,7 @@ EXAMPLES $ sf lightning dev component --name myComponent ``` -_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.1/src/commands/lightning/dev/component.ts)_ +_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.2-alpha.0/src/commands/lightning/dev/component.ts)_ ## `sf lightning dev site` @@ -305,6 +305,6 @@ EXAMPLES $ sf lightning dev site --name "Partner Central" --target-org myOrg --get-latest ``` -_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.1/src/commands/lightning/dev/site.ts)_ +_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.2-alpha.0/src/commands/lightning/dev/site.ts)_ diff --git a/package.json b/package.json index fa113e5..eb70cfd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/plugin-lightning-dev", "description": "Lightning development tools for LEX, Mobile, and Experience Sites", - "version": "4.5.1", + "version": "4.5.2-alpha.0", "author": "Salesforce", "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { From cbda11ca7e137a0b000e4820a5a8ed95e137c608 Mon Sep 17 00:00:00 2001 From: Nicolas Kruk Date: Sun, 12 Oct 2025 15:21:30 -0700 Subject: [PATCH 3/4] fix: auto enable local dev --- src/commands/lightning/dev/component.ts | 10 + src/shared/metaUtils.ts | 230 +++++++++++++++++++ test/shared/metaUtils.test.ts | 291 ++++++++++++++++++++++++ 3 files changed, 531 insertions(+) create mode 100644 src/shared/metaUtils.ts create mode 100644 test/shared/metaUtils.test.ts diff --git a/src/commands/lightning/dev/component.ts b/src/commands/lightning/dev/component.ts index fcd8cb1..1aca172 100644 --- a/src/commands/lightning/dev/component.ts +++ b/src/commands/lightning/dev/component.ts @@ -13,6 +13,7 @@ import { ComponentUtils } from '../../../shared/componentUtils.js'; import { PromptUtils } from '../../../shared/promptUtils.js'; import { PreviewUtils } from '../../../shared/previewUtils.js'; import { startLWCServer } from '../../../lwc-dev-server/index.js'; +import { MetaUtils } from '../../../shared/metaUtils.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.component'); @@ -65,6 +66,15 @@ export default class LightningDevComponent extends SfCommand; +}; + +/** + * Utility class for managing Salesforce metadata settings related to Lightning Development. + */ +export class MetaUtils { + private static logger = Logger.childFromRoot('metaUtils'); + + /** + * Retrieves the Lightning Experience Settings metadata from the org. + * + * @param connection the connection to the org + * @returns LightningExperienceSettingsMetadata object containing the settings + * @throws Error if unable to retrieve the metadata + */ + public static async getLightningExperienceSettings( + connection: Connection + ): Promise { + this.logger.debug('Retrieving Lightning Experience Settings metadata'); + + const metadata = await connection.metadata.read('LightningExperienceSettings', 'enableLightningPreviewPref'); + + if (!metadata) { + throw new Error('Unable to retrieve Lightning Experience Settings metadata.'); + } + + if (Array.isArray(metadata)) { + if (metadata.length === 0) { + throw new Error('Lightning Experience Settings metadata response was empty.'); + } + return metadata[0] as LightningExperienceSettingsMetadata; + } + + return metadata as LightningExperienceSettingsMetadata; + } + + /** + * Checks if Lightning Preview (Local Dev) is enabled for the org. + * + * @param connection the connection to the org + * @returns boolean indicating whether Lightning Preview is enabled + */ + public static async isLightningPreviewEnabled(connection: Connection): Promise { + try { + const settings = await this.getLightningExperienceSettings(connection); + const flagValue = settings.enableLightningPreviewPref ?? 'false'; + const enabled = String(flagValue).toLowerCase().trim() === 'true'; + this.logger.debug(`Lightning Preview enabled: ${enabled}`); + return enabled; + } catch (error) { + this.logger.warn('Error checking Lightning Preview status, assuming disabled:', error); + return false; + } + } + + /** + * Enables or disables Lightning Preview (Local Dev) for the org by updating the metadata. + * + * @param connection the connection to the org + * @param enable boolean indicating whether to enable (true) or disable (false) Lightning Preview + * @throws Error if the metadata update fails + */ + public static async setLightningPreviewEnabled(connection: Connection, enable: boolean): Promise { + this.logger.debug(`Setting Lightning Preview enabled to: ${enable}`); + + const updateResult = await connection.metadata.update('LightningExperienceSettings', { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: enable ? 'true' : 'false', + }); + + const results = Array.isArray(updateResult) ? updateResult : [updateResult]; + const typedResults = results as MetadataUpdateResult[]; + const errors = typedResults.filter((result) => !result.success); + + if (errors.length > 0) { + const message = errors + .flatMap((result) => (Array.isArray(result.errors) ? result.errors : result.errors ? [result.errors] : [])) + .filter((error): error is { message: string } => Boolean(error)) + .map((error) => error.message) + .join(' '); + + throw new Error(message || 'Failed to update Lightning Preview setting.'); + } + + this.logger.debug('Successfully updated Lightning Preview setting'); + } + + /** + * Retrieves the My Domain Settings metadata from the org. + * + * @param connection the connection to the org + * @returns MyDomainSettingsMetadata object containing the settings + * @throws Error if unable to retrieve the metadata + */ + public static async getMyDomainSettings(connection: Connection): Promise { + this.logger.debug('Retrieving My Domain Settings metadata'); + + const metadata = await connection.metadata.read('MyDomainSettings', 'MyDomain'); + + if (!metadata) { + throw new Error('Unable to retrieve My Domain settings metadata.'); + } + + if (Array.isArray(metadata)) { + if (metadata.length === 0) { + throw new Error('My Domain settings metadata response was empty.'); + } + return metadata[0] as MyDomainSettingsMetadata; + } + + return metadata as MyDomainSettingsMetadata; + } + + /** + * Checks if first-party cookies are required for the org. + * + * @param connection the connection to the org + * @returns boolean indicating whether first-party cookies are required + */ + public static async isFirstPartyCookieRequired(connection: Connection): Promise { + try { + const settings = await this.getMyDomainSettings(connection); + const flagValue = settings.isFirstPartyCookieUseRequired ?? 'false'; + const required = String(flagValue).toLowerCase().trim() === 'true'; + this.logger.debug(`First-party cookie required: ${required}`); + return required; + } catch (error) { + this.logger.warn('Error checking first-party cookie requirement, assuming not required:', error); + return false; + } + } + + /** + * Updates the My Domain setting that controls whether first-party cookies are required. + * + * @param connection the connection to the org + * @param requireFirstPartyCookies boolean indicating whether to require first-party cookies + * @throws Error if the metadata update fails + */ + public static async setMyDomainFirstPartyCookieRequirement( + connection: Connection, + requireFirstPartyCookies: boolean + ): Promise { + this.logger.debug(`Setting first-party cookie requirement to: ${requireFirstPartyCookies}`); + + const updateResult = await connection.metadata.update('MyDomainSettings', { + fullName: 'MyDomain', + isFirstPartyCookieUseRequired: requireFirstPartyCookies ? 'true' : 'false', + }); + + const results = Array.isArray(updateResult) ? updateResult : [updateResult]; + const typedResults = results as MetadataUpdateResult[]; + const errors = typedResults.filter((result) => !result.success); + + if (errors.length > 0) { + const message = errors + .flatMap((result) => (Array.isArray(result.errors) ? result.errors : result.errors ? [result.errors] : [])) + .filter((error): error is { message: string } => Boolean(error)) + .map((error) => error.message) + .join(' '); + + throw new Error(message || 'Failed to update My Domain first-party cookie requirement.'); + } + + this.logger.debug('Successfully updated first-party cookie requirement'); + } + + /** + * Ensures Lightning Preview is enabled for the org. If it's not enabled, this method will enable it. + * + * @param connection the connection to the org + * @returns boolean indicating whether Lightning Preview was already enabled (true) or had to be enabled (false) + */ + public static async ensureLightningPreviewEnabled(connection: Connection): Promise { + const isEnabled = await this.isLightningPreviewEnabled(connection); + + if (!isEnabled) { + this.logger.info('Lightning Preview is not enabled. Enabling it now...'); + await this.setLightningPreviewEnabled(connection, true); + return false; + } + + this.logger.debug('Lightning Preview is already enabled'); + return true; + } + + /** + * Ensures first-party cookies are not required for the org. If they are required, this method will disable the requirement. + * + * @param connection the connection to the org + * @returns boolean indicating whether first-party cookies were already not required (true) or had to be disabled (false) + */ + public static async ensureFirstPartyCookiesNotRequired(connection: Connection): Promise { + const isRequired = await this.isFirstPartyCookieRequired(connection); + + if (isRequired) { + this.logger.info('First-party cookies are required. Disabling requirement...'); + await this.setMyDomainFirstPartyCookieRequirement(connection, false); + return false; + } + + this.logger.debug('First-party cookies are not required'); + return true; + } +} diff --git a/test/shared/metaUtils.test.ts b/test/shared/metaUtils.test.ts new file mode 100644 index 0000000..4a9e979 --- /dev/null +++ b/test/shared/metaUtils.test.ts @@ -0,0 +1,291 @@ +/* + * 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 { expect } from 'chai'; +import { AuthInfo, Connection } from '@salesforce/core'; +import { TestContext } from '@salesforce/core/testSetup'; +import { MetaUtils } from '../../src/shared/metaUtils.js'; + +describe('MetaUtils', () => { + const $$ = new TestContext(); + + afterEach(() => { + $$.restore(); + }); + + describe('getLightningExperienceSettings', () => { + it('should return Lightning Experience Settings metadata', async () => { + const mockMetadata = { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'true', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.getLightningExperienceSettings(connection); + + expect(result).to.deep.equal(mockMetadata); + }); + + it('should handle array response', async () => { + const mockMetadata = [ + { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'true', + }, + ]; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.getLightningExperienceSettings(connection); + + expect(result).to.deep.equal(mockMetadata[0]); + }); + + it('should throw error if metadata is null', async () => { + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(null), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + + try { + await MetaUtils.getLightningExperienceSettings(connection); + expect.fail('Should have thrown an error'); + } catch (error) { + expect((error as Error).message).to.equal('Unable to retrieve Lightning Experience Settings metadata.'); + } + }); + + it('should throw error if array response is empty', async () => { + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves([]), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + + try { + await MetaUtils.getLightningExperienceSettings(connection); + expect.fail('Should have thrown an error'); + } catch (error) { + expect((error as Error).message).to.equal('Lightning Experience Settings metadata response was empty.'); + } + }); + }); + + describe('isLightningPreviewEnabled', () => { + it('should return true when Lightning Preview is enabled', async () => { + const mockMetadata = { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'true', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.isLightningPreviewEnabled(connection); + + expect(result).to.be.true; + }); + + it('should return false when Lightning Preview is disabled', async () => { + const mockMetadata = { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'false', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.isLightningPreviewEnabled(connection); + + expect(result).to.be.false; + }); + + it('should return false when enableLightningPreviewPref is undefined', async () => { + const mockMetadata = { + fullName: 'enableLightningPreviewPref', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.isLightningPreviewEnabled(connection); + + expect(result).to.be.false; + }); + + it('should return false on error', async () => { + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().rejects(new Error('Network error')), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.isLightningPreviewEnabled(connection); + + expect(result).to.be.false; + }); + }); + + describe('setLightningPreviewEnabled', () => { + it('should enable Lightning Preview successfully', async () => { + const mockUpdateResult = { + success: true, + fullName: 'enableLightningPreviewPref', + }; + + const updateStub = $$.SANDBOX.stub().resolves(mockUpdateResult); + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + update: updateStub, + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + await MetaUtils.setLightningPreviewEnabled(connection, true); + + expect( + updateStub.calledWith('LightningExperienceSettings', { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'true', + }) + ).to.be.true; + }); + + it('should disable Lightning Preview successfully', async () => { + const mockUpdateResult = { + success: true, + fullName: 'enableLightningPreviewPref', + }; + + const updateStub = $$.SANDBOX.stub().resolves(mockUpdateResult); + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + update: updateStub, + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + await MetaUtils.setLightningPreviewEnabled(connection, false); + + expect( + updateStub.calledWith('LightningExperienceSettings', { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'false', + }) + ).to.be.true; + }); + + it('should handle array response', async () => { + const mockUpdateResult = [ + { + success: true, + fullName: 'enableLightningPreviewPref', + }, + ]; + + const updateStub = $$.SANDBOX.stub().resolves(mockUpdateResult); + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + update: updateStub, + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + await MetaUtils.setLightningPreviewEnabled(connection, true); + + expect(updateStub.called).to.be.true; + }); + + it('should throw error when update fails', async () => { + const mockUpdateResult = { + success: false, + fullName: 'enableLightningPreviewPref', + errors: [{ message: 'Update failed' }], + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + update: $$.SANDBOX.stub().resolves(mockUpdateResult), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + + try { + await MetaUtils.setLightningPreviewEnabled(connection, true); + expect.fail('Should have thrown an error'); + } catch (error) { + expect((error as Error).message).to.equal('Update failed'); + } + }); + + it('should throw generic error when update fails without error message', async () => { + const mockUpdateResult = { + success: false, + fullName: 'enableLightningPreviewPref', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + update: $$.SANDBOX.stub().resolves(mockUpdateResult), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + + try { + await MetaUtils.setLightningPreviewEnabled(connection, true); + expect.fail('Should have thrown an error'); + } catch (error) { + expect((error as Error).message).to.equal('Failed to update Lightning Preview setting.'); + } + }); + }); + + describe('ensureLightningPreviewEnabled', () => { + it('should return true if Lightning Preview is already enabled', async () => { + const mockMetadata = { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'true', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.ensureLightningPreviewEnabled(connection); + + expect(result).to.be.true; + }); + + it('should enable Lightning Preview and return false if it was disabled', async () => { + const mockMetadata = { + fullName: 'enableLightningPreviewPref', + enableLightningPreviewPref: 'false', + }; + + const mockUpdateResult = { + success: true, + fullName: 'enableLightningPreviewPref', + }; + + $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ + read: $$.SANDBOX.stub().resolves(mockMetadata), + update: $$.SANDBOX.stub().resolves(mockUpdateResult), + }); + + const connection = new Connection({ authInfo: new AuthInfo() }); + const result = await MetaUtils.ensureLightningPreviewEnabled(connection); + + expect(result).to.be.false; + }); + }); +}); From c8c7c7adea3f3c3739e51fa716277156ce266c6b Mon Sep 17 00:00:00 2001 From: Nicolas Kruk Date: Sun, 12 Oct 2025 15:22:35 -0700 Subject: [PATCH 4/4] fix: no tests --- test/shared/metaUtils.test.ts | 291 ---------------------------------- 1 file changed, 291 deletions(-) delete mode 100644 test/shared/metaUtils.test.ts diff --git a/test/shared/metaUtils.test.ts b/test/shared/metaUtils.test.ts deleted file mode 100644 index 4a9e979..0000000 --- a/test/shared/metaUtils.test.ts +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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 { expect } from 'chai'; -import { AuthInfo, Connection } from '@salesforce/core'; -import { TestContext } from '@salesforce/core/testSetup'; -import { MetaUtils } from '../../src/shared/metaUtils.js'; - -describe('MetaUtils', () => { - const $$ = new TestContext(); - - afterEach(() => { - $$.restore(); - }); - - describe('getLightningExperienceSettings', () => { - it('should return Lightning Experience Settings metadata', async () => { - const mockMetadata = { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'true', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.getLightningExperienceSettings(connection); - - expect(result).to.deep.equal(mockMetadata); - }); - - it('should handle array response', async () => { - const mockMetadata = [ - { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'true', - }, - ]; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.getLightningExperienceSettings(connection); - - expect(result).to.deep.equal(mockMetadata[0]); - }); - - it('should throw error if metadata is null', async () => { - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(null), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - - try { - await MetaUtils.getLightningExperienceSettings(connection); - expect.fail('Should have thrown an error'); - } catch (error) { - expect((error as Error).message).to.equal('Unable to retrieve Lightning Experience Settings metadata.'); - } - }); - - it('should throw error if array response is empty', async () => { - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves([]), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - - try { - await MetaUtils.getLightningExperienceSettings(connection); - expect.fail('Should have thrown an error'); - } catch (error) { - expect((error as Error).message).to.equal('Lightning Experience Settings metadata response was empty.'); - } - }); - }); - - describe('isLightningPreviewEnabled', () => { - it('should return true when Lightning Preview is enabled', async () => { - const mockMetadata = { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'true', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.isLightningPreviewEnabled(connection); - - expect(result).to.be.true; - }); - - it('should return false when Lightning Preview is disabled', async () => { - const mockMetadata = { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'false', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.isLightningPreviewEnabled(connection); - - expect(result).to.be.false; - }); - - it('should return false when enableLightningPreviewPref is undefined', async () => { - const mockMetadata = { - fullName: 'enableLightningPreviewPref', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.isLightningPreviewEnabled(connection); - - expect(result).to.be.false; - }); - - it('should return false on error', async () => { - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().rejects(new Error('Network error')), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.isLightningPreviewEnabled(connection); - - expect(result).to.be.false; - }); - }); - - describe('setLightningPreviewEnabled', () => { - it('should enable Lightning Preview successfully', async () => { - const mockUpdateResult = { - success: true, - fullName: 'enableLightningPreviewPref', - }; - - const updateStub = $$.SANDBOX.stub().resolves(mockUpdateResult); - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - update: updateStub, - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - await MetaUtils.setLightningPreviewEnabled(connection, true); - - expect( - updateStub.calledWith('LightningExperienceSettings', { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'true', - }) - ).to.be.true; - }); - - it('should disable Lightning Preview successfully', async () => { - const mockUpdateResult = { - success: true, - fullName: 'enableLightningPreviewPref', - }; - - const updateStub = $$.SANDBOX.stub().resolves(mockUpdateResult); - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - update: updateStub, - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - await MetaUtils.setLightningPreviewEnabled(connection, false); - - expect( - updateStub.calledWith('LightningExperienceSettings', { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'false', - }) - ).to.be.true; - }); - - it('should handle array response', async () => { - const mockUpdateResult = [ - { - success: true, - fullName: 'enableLightningPreviewPref', - }, - ]; - - const updateStub = $$.SANDBOX.stub().resolves(mockUpdateResult); - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - update: updateStub, - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - await MetaUtils.setLightningPreviewEnabled(connection, true); - - expect(updateStub.called).to.be.true; - }); - - it('should throw error when update fails', async () => { - const mockUpdateResult = { - success: false, - fullName: 'enableLightningPreviewPref', - errors: [{ message: 'Update failed' }], - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - update: $$.SANDBOX.stub().resolves(mockUpdateResult), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - - try { - await MetaUtils.setLightningPreviewEnabled(connection, true); - expect.fail('Should have thrown an error'); - } catch (error) { - expect((error as Error).message).to.equal('Update failed'); - } - }); - - it('should throw generic error when update fails without error message', async () => { - const mockUpdateResult = { - success: false, - fullName: 'enableLightningPreviewPref', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - update: $$.SANDBOX.stub().resolves(mockUpdateResult), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - - try { - await MetaUtils.setLightningPreviewEnabled(connection, true); - expect.fail('Should have thrown an error'); - } catch (error) { - expect((error as Error).message).to.equal('Failed to update Lightning Preview setting.'); - } - }); - }); - - describe('ensureLightningPreviewEnabled', () => { - it('should return true if Lightning Preview is already enabled', async () => { - const mockMetadata = { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'true', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.ensureLightningPreviewEnabled(connection); - - expect(result).to.be.true; - }); - - it('should enable Lightning Preview and return false if it was disabled', async () => { - const mockMetadata = { - fullName: 'enableLightningPreviewPref', - enableLightningPreviewPref: 'false', - }; - - const mockUpdateResult = { - success: true, - fullName: 'enableLightningPreviewPref', - }; - - $$.SANDBOX.stub(Connection.prototype, 'metadata').value({ - read: $$.SANDBOX.stub().resolves(mockMetadata), - update: $$.SANDBOX.stub().resolves(mockUpdateResult), - }); - - const connection = new Connection({ authInfo: new AuthInfo() }); - const result = await MetaUtils.ensureLightningPreviewEnabled(connection); - - expect(result).to.be.false; - }); - }); -});