Skip to content

feat: ensure local dev is enabled for an org #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions messages/shared.utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ The SSL certificate data to be used by the local dev server for secure connectio
# config-utils.cert-error-message

You must provide valid SSL certificate data

# error.localdev.not.enabled

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.
6 changes: 6 additions & 0 deletions src/commands/lightning/dev/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ConfigUtils, IdentityTokenService } from '../../../shared/configUtils.j

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

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

const localDevEnabled = await OrgUtils.isLocalDevEnabled(connection);
if (!localDevEnabled) {
return Promise.reject(new Error(sharedMessages.getMessage('error.localdev.not.enabled')));
}

const tokenService = new AppServerIdentityTokenService(connection);
const token = await ConfigUtils.getOrCreateIdentityToken(username, tokenService);

Expand Down
7 changes: 7 additions & 0 deletions src/commands/lightning/dev/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import fs from 'node:fs';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { expDev } from '@lwrjs/api';
import { OrgUtils } from '../../../shared/orgUtils.js';
import { PromptUtils } from '../../../shared/prompt.js';
import { ExperienceSite } from '../../../shared/experience/expSite.js';

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

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

const localDevEnabled = await OrgUtils.isLocalDevEnabled(org.getConnection(undefined));
if (!localDevEnabled) {
throw new Error(sharedMessages.getMessage('error.localdev.not.enabled'));
}

// If user doesn't specify a site, prompt the user for one
if (!siteName) {
const allSites = await ExperienceSite.getAllExpSites(org);
Expand Down
17 changes: 17 additions & 0 deletions src/shared/orgUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

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

type LightningPreviewMetadataResponse = {
enableLightningPreviewPref?: string;
};

export class OrgUtils {
/**
* Given an app name, it queries the org to find the matching app id. To do so,
Expand Down Expand Up @@ -38,4 +42,17 @@ export class OrgUtils {

return undefined;
}

/**
* Checks to see if Local Dev is enabled for the org.
*
* @param connection the connection to the org
* @returns boolean indicating whether Local Dev is enabled for the org.
*/
public static async isLocalDevEnabled(connection: Connection): Promise<boolean> {
const metadata = await connection.metadata.read('LightningExperienceSettings', 'enableLightningPreviewPref');
const flagValue = (metadata as LightningPreviewMetadataResponse).enableLightningPreviewPref ?? 'false';
const localDevEnabled = flagValue.toLowerCase().trim() === 'true';
return localDevEnabled;
}
}
51 changes: 12 additions & 39 deletions test/commands/lightning/dev/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);

describe('lightning dev app', () => {
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.app');
const sharedMessages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'shared.utils');
const $$ = new TestContext();
const testOrgData = new MockTestOrgData();
const testAppId = '06m8b000002vpFSAAY';
Expand Down Expand Up @@ -83,6 +84,7 @@ describe('lightning dev app', () => {
$$.SANDBOX.stub(SfConfig.prototype, 'set');
$$.SANDBOX.stub(SfConfig.prototype, 'write').resolves();
$$.SANDBOX.stub(ConfigUtils, 'getOrCreateIdentityToken').resolves(fakeIdentityToken);
$$.SANDBOX.stub(OrgUtils, 'isLocalDevEnabled').resolves(true);

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

it('throws when local dev not enabled', async () => {
try {
$$.SANDBOX.restore();
$$.SANDBOX.stub(OrgUtils, 'isLocalDevEnabled').resolves(false);
await MockedLightningPreviewApp.run(['--name', 'blah', '-o', testOrgData.username]);
} catch (err) {
expect(err).to.be.an('error').with.property('message', sharedMessages.getMessage('error.localdev.not.enabled'));
}
});

it('throws when app not found', async () => {
try {
$$.SANDBOX.stub(OrgUtils, 'getAppId').resolves(undefined);
Expand Down Expand Up @@ -226,24 +238,6 @@ describe('lightning dev app', () => {
await verifyMobileThrowsWhenFailedToGenerateCert(Platform.android);
});

/* it('waits for user to manually install the certificate', async () => {
$$.SANDBOX.stub(OrgUtils, 'getAppId').resolves(testAppId);
$$.SANDBOX.stub(PreviewUtils, 'generateWebSocketUrlForLocalDevServer').returns(testServerUrl);
$$.SANDBOX.stub(ConfigUtils, 'getIdentityData').resolves(fakeIdentityData);

$$.SANDBOX.stub(LwcDevMobileCoreSetup.prototype, 'init').resolves();
$$.SANDBOX.stub(LwcDevMobileCoreSetup.prototype, 'run').resolves();

$$.SANDBOX.stub(PreviewUtils, 'getMobileDevice').callsFake((platform) =>
Promise.resolve(platform === Platform.ios ? testIOSDevice : testAndroidDevice)
);

$$.SANDBOX.stub(PreviewUtils, 'generateSelfSignedCert').resolves(certData);

await verifyMobileWaitsForManualCertInstallation(Platform.ios);
await verifyMobileWaitsForManualCertInstallation(Platform.android);
});*/

it('throws if user chooses not to install app on mobile device', async () => {
$$.SANDBOX.stub(OrgUtils, 'getAppId').resolves(testAppId);
$$.SANDBOX.stub(PreviewUtils, 'generateWebSocketUrlForLocalDevServer').returns(testServerUrl);
Expand Down Expand Up @@ -332,27 +326,6 @@ describe('lightning dev app', () => {
}
}

/* async function verifyMobileWaitsForManualCertInstallation(platform: Platform.ios | Platform.android) {
const installCertStub =
platform === Platform.ios
? $$.SANDBOX.stub(AppleDevice.prototype, 'installCert').resolves()
: $$.SANDBOX.stub(AndroidDevice.prototype, 'installCert').resolves();

if (platform === Platform.ios) {
$$.SANDBOX.stub(AppleDevice.prototype, 'boot').resolves();
$$.SANDBOX.stub(AppleDevice.prototype, 'isAppInstalled').resolves(true);
$$.SANDBOX.stub(AppleDevice.prototype, 'launchApp').resolves();
} else {
$$.SANDBOX.stub(AndroidDevice.prototype, 'boot').resolves();
$$.SANDBOX.stub(AndroidDevice.prototype, 'isAppInstalled').resolves(true);
$$.SANDBOX.stub(AndroidDevice.prototype, 'launchApp').resolves();
$$.SANDBOX.stub(AndroidDevice.prototype, 'isCertInstalled').resolves(false);
}

await MockedLightningPreviewApp.run(['-n', 'Sales', '-o', testOrgData.username, '-t', platform]);
expect(installCertStub.called).to.be.true;
}*/

async function verifyMobileThrowsWhenUserDeclinesToInstallApp(platform: Platform.ios | Platform.android) {
if (platform === Platform.ios) {
$$.SANDBOX.stub(AppleDevice.prototype, 'boot').resolves();
Expand Down