Skip to content

Commit c5749ec

Browse files
authored
feat: ensure local dev is enabled for an org (#189)
* feat: ensure local dev is enabled for an org * chore: address PR feedback
1 parent cba8d7a commit c5749ec

File tree

5 files changed

+46
-39
lines changed

5 files changed

+46
-39
lines changed

messages/shared.utils.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ The SSL certificate data to be used by the local dev server for secure connectio
2525
# config-utils.cert-error-message
2626

2727
You must provide valid SSL certificate data
28+
29+
# error.localdev.not.enabled
30+
31+
Local Dev is not enabled for your org. See https://developer.salesforce.com/docs/platform/lwc/guide/get-started-test-components.html for more information on enabling and using Local Dev.

src/commands/lightning/dev/app.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { ConfigUtils, IdentityTokenService } from '../../../shared/configUtils.j
2424

2525
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2626
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.app');
27+
const sharedMessages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'shared.utils');
2728

2829
export const iOSSalesforceAppPreviewConfig = {
2930
name: 'Salesforce Mobile App',
@@ -101,6 +102,11 @@ export default class LightningDevApp extends SfCommand<void> {
101102
return Promise.reject(new Error(messages.getMessage('error.username')));
102103
}
103104

105+
const localDevEnabled = await OrgUtils.isLocalDevEnabled(connection);
106+
if (!localDevEnabled) {
107+
return Promise.reject(new Error(sharedMessages.getMessage('error.localdev.not.enabled')));
108+
}
109+
104110
const tokenService = new AppServerIdentityTokenService(connection);
105111
const token = await ConfigUtils.getOrCreateIdentityToken(username, tokenService);
106112

src/commands/lightning/dev/site.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import fs from 'node:fs';
88
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
99
import { Messages } from '@salesforce/core';
1010
import { expDev } from '@lwrjs/api';
11+
import { OrgUtils } from '../../../shared/orgUtils.js';
1112
import { PromptUtils } from '../../../shared/prompt.js';
1213
import { ExperienceSite } from '../../../shared/experience/expSite.js';
1314

1415
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
1516
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.site');
17+
const sharedMessages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'shared.utils');
1618

1719
export default class LightningDevSite extends SfCommand<void> {
1820
public static readonly summary = messages.getMessage('summary');
@@ -35,6 +37,11 @@ export default class LightningDevSite extends SfCommand<void> {
3537
const org = flags['target-org'];
3638
let siteName = flags.name;
3739

40+
const localDevEnabled = await OrgUtils.isLocalDevEnabled(org.getConnection(undefined));
41+
if (!localDevEnabled) {
42+
throw new Error(sharedMessages.getMessage('error.localdev.not.enabled'));
43+
}
44+
3845
// If user doesn't specify a site, prompt the user for one
3946
if (!siteName) {
4047
const allSites = await ExperienceSite.getAllExpSites(org);

src/shared/orgUtils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
import { Connection } from '@salesforce/core';
99

10+
type LightningPreviewMetadataResponse = {
11+
enableLightningPreviewPref?: string;
12+
};
13+
1014
export class OrgUtils {
1115
/**
1216
* Given an app name, it queries the org to find the matching app id. To do so,
@@ -38,4 +42,17 @@ export class OrgUtils {
3842

3943
return undefined;
4044
}
45+
46+
/**
47+
* Checks to see if Local Dev is enabled for the org.
48+
*
49+
* @param connection the connection to the org
50+
* @returns boolean indicating whether Local Dev is enabled for the org.
51+
*/
52+
public static async isLocalDevEnabled(connection: Connection): Promise<boolean> {
53+
const metadata = await connection.metadata.read('LightningExperienceSettings', 'enableLightningPreviewPref');
54+
const flagValue = (metadata as LightningPreviewMetadataResponse).enableLightningPreviewPref ?? 'false';
55+
const localDevEnabled = flagValue.toLowerCase().trim() === 'true';
56+
return localDevEnabled;
57+
}
4158
}

test/commands/lightning/dev/app.test.ts

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
3737

3838
describe('lightning dev app', () => {
3939
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.app');
40+
const sharedMessages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'shared.utils');
4041
const $$ = new TestContext();
4142
const testOrgData = new MockTestOrgData();
4243
const testAppId = '06m8b000002vpFSAAY';
@@ -83,6 +84,7 @@ describe('lightning dev app', () => {
8384
$$.SANDBOX.stub(SfConfig.prototype, 'set');
8485
$$.SANDBOX.stub(SfConfig.prototype, 'write').resolves();
8586
$$.SANDBOX.stub(ConfigUtils, 'getOrCreateIdentityToken').resolves(fakeIdentityToken);
87+
$$.SANDBOX.stub(OrgUtils, 'isLocalDevEnabled').resolves(true);
8688

8789
MockedLightningPreviewApp = await esmock<typeof LightningDevApp>('../../../../src/commands/lightning/dev/app.js', {
8890
'../../../../src/lwc-dev-server/index.js': {
@@ -95,6 +97,16 @@ describe('lightning dev app', () => {
9597
$$.restore();
9698
});
9799

100+
it('throws when local dev not enabled', async () => {
101+
try {
102+
$$.SANDBOX.restore();
103+
$$.SANDBOX.stub(OrgUtils, 'isLocalDevEnabled').resolves(false);
104+
await MockedLightningPreviewApp.run(['--name', 'blah', '-o', testOrgData.username]);
105+
} catch (err) {
106+
expect(err).to.be.an('error').with.property('message', sharedMessages.getMessage('error.localdev.not.enabled'));
107+
}
108+
});
109+
98110
it('throws when app not found', async () => {
99111
try {
100112
$$.SANDBOX.stub(OrgUtils, 'getAppId').resolves(undefined);
@@ -226,24 +238,6 @@ describe('lightning dev app', () => {
226238
await verifyMobileThrowsWhenFailedToGenerateCert(Platform.android);
227239
});
228240

229-
/* it('waits for user to manually install the certificate', async () => {
230-
$$.SANDBOX.stub(OrgUtils, 'getAppId').resolves(testAppId);
231-
$$.SANDBOX.stub(PreviewUtils, 'generateWebSocketUrlForLocalDevServer').returns(testServerUrl);
232-
$$.SANDBOX.stub(ConfigUtils, 'getIdentityData').resolves(fakeIdentityData);
233-
234-
$$.SANDBOX.stub(LwcDevMobileCoreSetup.prototype, 'init').resolves();
235-
$$.SANDBOX.stub(LwcDevMobileCoreSetup.prototype, 'run').resolves();
236-
237-
$$.SANDBOX.stub(PreviewUtils, 'getMobileDevice').callsFake((platform) =>
238-
Promise.resolve(platform === Platform.ios ? testIOSDevice : testAndroidDevice)
239-
);
240-
241-
$$.SANDBOX.stub(PreviewUtils, 'generateSelfSignedCert').resolves(certData);
242-
243-
await verifyMobileWaitsForManualCertInstallation(Platform.ios);
244-
await verifyMobileWaitsForManualCertInstallation(Platform.android);
245-
});*/
246-
247241
it('throws if user chooses not to install app on mobile device', async () => {
248242
$$.SANDBOX.stub(OrgUtils, 'getAppId').resolves(testAppId);
249243
$$.SANDBOX.stub(PreviewUtils, 'generateWebSocketUrlForLocalDevServer').returns(testServerUrl);
@@ -332,27 +326,6 @@ describe('lightning dev app', () => {
332326
}
333327
}
334328

335-
/* async function verifyMobileWaitsForManualCertInstallation(platform: Platform.ios | Platform.android) {
336-
const installCertStub =
337-
platform === Platform.ios
338-
? $$.SANDBOX.stub(AppleDevice.prototype, 'installCert').resolves()
339-
: $$.SANDBOX.stub(AndroidDevice.prototype, 'installCert').resolves();
340-
341-
if (platform === Platform.ios) {
342-
$$.SANDBOX.stub(AppleDevice.prototype, 'boot').resolves();
343-
$$.SANDBOX.stub(AppleDevice.prototype, 'isAppInstalled').resolves(true);
344-
$$.SANDBOX.stub(AppleDevice.prototype, 'launchApp').resolves();
345-
} else {
346-
$$.SANDBOX.stub(AndroidDevice.prototype, 'boot').resolves();
347-
$$.SANDBOX.stub(AndroidDevice.prototype, 'isAppInstalled').resolves(true);
348-
$$.SANDBOX.stub(AndroidDevice.prototype, 'launchApp').resolves();
349-
$$.SANDBOX.stub(AndroidDevice.prototype, 'isCertInstalled').resolves(false);
350-
}
351-
352-
await MockedLightningPreviewApp.run(['-n', 'Sales', '-o', testOrgData.username, '-t', platform]);
353-
expect(installCertStub.called).to.be.true;
354-
}*/
355-
356329
async function verifyMobileThrowsWhenUserDeclinesToInstallApp(platform: Platform.ios | Platform.android) {
357330
if (platform === Platform.ios) {
358331
$$.SANDBOX.stub(AppleDevice.prototype, 'boot').resolves();

0 commit comments

Comments
 (0)