Skip to content

Commit 69c2cbe

Browse files
authored
Merge branch 'main' into js/edits
2 parents 008fb37 + 0462fff commit 69c2cbe

33 files changed

Lines changed: 941 additions & 378 deletions

.github/workflows/test.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ jobs:
3131
nuts:
3232
needs: linux-unit-tests
3333
uses: salesforcecli/github-workflows/.github/workflows/nut.yml@main
34-
secrets: inherit
3534
strategy:
3635
matrix:
3736
os: [ubuntu-latest, windows-latest]
3837
fail-fast: false
3938
with:
4039
os: ${{ matrix.os }}
40+
command: yarn test:nuts
41+
retries: 5
42+
secrets: inherit

.vscode/launch.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,36 @@
4040
"smartStep": true,
4141
"internalConsoleOptions": "openOnSessionStart",
4242
"preLaunchTask": "Compile tests"
43+
},
44+
{
45+
"name": "Run Nuts Test",
46+
"type": "node",
47+
"request": "launch",
48+
"runtimeExecutable": "node",
49+
"runtimeArgs": [
50+
"--inspect-brk",
51+
"--no-deprecation",
52+
"--no-warnings",
53+
"-r",
54+
"dotenv/config",
55+
"--loader",
56+
"ts-node/esm",
57+
"--loader",
58+
"esmock"
59+
],
60+
"program": "${workspaceFolder}/node_modules/mocha/lib/cli/cli.js",
61+
"args": ["${file}", "--slow", "4500", "--timeout", "600000"],
62+
"cwd": "${workspaceFolder}",
63+
"env": {
64+
"NODE_ENV": "development",
65+
"SFDX_ENV": "development",
66+
"TS_NODE_PROJECT": "test/tsconfig.json"
67+
},
68+
"sourceMaps": true,
69+
"skipFiles": ["<node_internals>/**"],
70+
"internalConsoleOptions": "openOnSessionStart",
71+
"console": "integratedTerminal",
72+
"preLaunchTask": "Compile plugin only"
4373
}
4474
]
4575
}

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## [6.2.15](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.14...6.2.15) (2026-02-28)
2+
3+
### Bug Fixes
4+
5+
- **deps:** bump rollup from 2.79.2 to 2.80.0 ([#636](https://github.com/salesforcecli/plugin-lightning-dev/issues/636)) ([78b37f8](https://github.com/salesforcecli/plugin-lightning-dev/commit/78b37f81e4986a7c04ad550f235fbcdacdb1d92b))
6+
7+
## [6.2.14](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.13...6.2.14) (2026-02-28)
8+
9+
### Bug Fixes
10+
11+
- **deps:** bump koa from 3.1.1 to 3.1.2 ([#637](https://github.com/salesforcecli/plugin-lightning-dev/issues/637)) ([ebba750](https://github.com/salesforcecli/plugin-lightning-dev/commit/ebba750873a919da18f52185d22b5c84f3905f05))
12+
113
## [6.2.13](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.12...6.2.13) (2026-02-27)
214

315
## [6.2.12](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.11...6.2.12) (2026-02-22)

README.md

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,53 @@ yarn && yarn build
135135
yarn update-snapshots
136136
```
137137

138+
## e2e Org configuration (for NUTs)
139+
140+
If a new org is required for NUTs tests, these are the steps to create and configure one.
141+
142+
1. Create a new STM org in [Org Farm](https://orgfarm.salesforce.com/farms)
143+
2. [Enable DevHub for Scratch Org Creation](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_setup_enable_devhub.htm)
144+
3. Enable the following org perm: CreateConnectedApps
145+
4. Increase the following org limits to 99: ProvScratchActiveLimit, ProvScratchDailyLimit, ScratchRequestActiveCount, ScratchRequestActiveLimit, ScratchRequestDailyLimit
146+
5. [Create a Connected App](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm)
147+
6. Enable JWT authentication and [test it](https://developer.salesforce.com/docs/atlas.en-us.260.0.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_org_commands_unified.htm#cli_reference_org_login_jwt_unified)
148+
7. Use the credentials as values for the respective NUTS environment variables.
149+
150+
## Running NUTs (integration tests) locally
151+
152+
NUTs (e2e integration tests) run the plugin against a real org and, for component-preview tests, a real browser (Playwright). To run them locally:
153+
154+
1. **Environment variables**
155+
Copy `.env.template` to `.env` and set the values for your Dev Hub org and test setup:
156+
- `TESTKIT_JWT_KEY` - ./server.key from JWT configuration
157+
- `TESTKIT_JWT_CLIENT_ID` – Client id from JWT configuration
158+
- `TESTKIT_HUB_USERNAME` - Dev Hub username
159+
- `TESTKIT_HUB_INSTANCE` – Dev Hub login URL
160+
161+
2. **Run all NUTs** (loads variables from `.env` via `dotenv/config`):
162+
163+
```bash
164+
yarn test:nuts:local
165+
```
166+
167+
3. **Run a single NUT file** (e.g. one test file):
168+
169+
```bash
170+
yarn test:nut:local path/to/file.nut.ts
171+
```
172+
173+
4. **Run with a visible browser** (headed mode) for debugging:
174+
175+
```bash
176+
HEADED=true yarn test:nuts:local
177+
```
178+
179+
or, for a single file:
180+
181+
```bash
182+
HEADED=true yarn test:nut:local path/to/file.nut.ts
183+
```
184+
138185
## Commands
139186

140187
<!-- commands -->
@@ -203,7 +250,7 @@ EXAMPLES
203250
$ sf lightning dev app --target-org myOrg --device-type ios --device-id "iPhone 15 Pro Max"
204251
```
205252

206-
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.13/src/commands/lightning/dev/app.ts)_
253+
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.15/src/commands/lightning/dev/app.ts)_
207254

208255
## `sf lightning dev component`
209256

@@ -251,7 +298,7 @@ EXAMPLES
251298
$ sf lightning dev component --name myComponent
252299
```
253300

254-
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.13/src/commands/lightning/dev/component.ts)_
301+
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.15/src/commands/lightning/dev/component.ts)_
255302

256303
## `sf lightning dev site`
257304

@@ -300,6 +347,6 @@ EXAMPLES
300347
$ sf lightning dev site --name "Partner Central" --target-org myOrg
301348
```
302349

303-
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.13/src/commands/lightning/dev/site.ts)_
350+
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.15/src/commands/lightning/dev/site.ts)_
304351

305352
<!-- commandsstop -->

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/plugin-lightning-dev",
33
"description": "Lightning development tools for LEX, Mobile, and Experience Sites",
4-
"version": "6.2.13",
4+
"version": "6.2.15",
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
@@ -31,6 +31,8 @@
3131
"@types/node-fetch": "^2.6.13",
3232
"@types/xml2js": "^0.4.14",
3333
"@typescript-eslint/eslint-plugin": "^6.21.0",
34+
"@playwright/test": "^1.49.0",
35+
"playwright": "^1.49.0",
3436
"dotenv": "^16.5.0",
3537
"eslint": "^8.57.0",
3638
"eslint-config-prettier": "^9.1.2",
@@ -100,11 +102,14 @@
100102
"link-check": "wireit",
101103
"link-lwr": "yarn link @lwrjs/api @lwrjs/app-service @lwrjs/asset-registry @lwrjs/asset-transformer @lwrjs/auth-middleware @lwrjs/base-view-provider @lwrjs/base-view-transformer @lwrjs/client-modules @lwrjs/config @lwrjs/core @lwrjs/dev-proxy-server @lwrjs/diagnostics @lwrjs/esbuild @lwrjs/everywhere @lwrjs/fs-asset-provider @lwrjs/fs-watch @lwrjs/html-view-provider @lwrjs/instrumentation @lwrjs/label-module-provider @lwrjs/lambda @lwrjs/legacy-npm-module-provider @lwrjs/loader @lwrjs/lwc-module-provider @lwrjs/lwc-ssr @lwrjs/markdown-view-provider @lwrjs/module-bundler @lwrjs/module-registry @lwrjs/npm-module-provider @lwrjs/nunjucks-view-provider @lwrjs/o11y @lwrjs/resource-registry @lwrjs/router @lwrjs/security @lwrjs/server @lwrjs/shared-utils @lwrjs/static @lwrjs/tools @lwrjs/types @lwrjs/view-registry lwr",
102104
"lint": "wireit",
105+
"postinstall": "npx playwright install --with-deps",
103106
"postpack": "sf-clean --ignore-signing-artifacts",
104107
"prepack": "sf-prepack",
105108
"prepare": "sf-install",
106109
"test": "wireit",
107-
"test:nuts": "nyc mocha \"**/*.nut.ts\" --slow 4500 --timeout 600000 --parallel",
110+
"test:nuts": "mocha \"**/*.nut.ts\" --slow 30000 --timeout 600000 --parallel=false",
111+
"test:nuts:local": "node -r dotenv/config ./node_modules/.bin/nyc mocha \"**/*.nut.ts\" --slow 30000 --timeout 600000 --parallel=false",
112+
"test:nut:local": "node -r dotenv/config ./node_modules/.bin/nyc mocha --slow 30000 --timeout 600000",
108113
"test:only": "wireit",
109114
"unlink-lwr": "yarn unlink @lwrjs/api @lwrjs/app-service @lwrjs/asset-registry @lwrjs/asset-transformer @lwrjs/auth-middleware @lwrjs/base-view-provider @lwrjs/base-view-transformer @lwrjs/client-modules @lwrjs/config @lwrjs/core @lwrjs/dev-proxy-server @lwrjs/diagnostics @lwrjs/esbuild @lwrjs/everywhere @lwrjs/fs-asset-provider @lwrjs/fs-watch @lwrjs/html-view-provider @lwrjs/instrumentation @lwrjs/label-module-provider @lwrjs/lambda @lwrjs/legacy-npm-module-provider @lwrjs/loader @lwrjs/lwc-module-provider @lwrjs/lwc-ssr @lwrjs/markdown-view-provider @lwrjs/module-bundler @lwrjs/module-registry @lwrjs/npm-module-provider @lwrjs/nunjucks-view-provider @lwrjs/o11y @lwrjs/resource-registry @lwrjs/router @lwrjs/security @lwrjs/server @lwrjs/shared-utils @lwrjs/static @lwrjs/tools @lwrjs/types @lwrjs/view-registry lwr",
110115
"update-snapshots": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" snapshot:generate",

src/commands/lightning/dev/component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ export default class LightningDevComponent extends SfCommand<ComponentPreviewRes
186186
await this.config.runCommand('org:open', launchArguments);
187187
}
188188

189+
// Emit preview URL for tests (e.g. NUTs that drive Playwright against the preview page)
190+
if (process.env.LIGHTNING_DEV_PRINT_PREVIEW_URL === 'true') {
191+
this.log(previewUrl);
192+
}
193+
189194
return result;
190195
}
191196
}

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

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2026, Salesforce, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import type { ChildProcessByStdio } from 'node:child_process';
18+
import type { Readable, Writable } from 'node:stream';
19+
import { TestSession } from '@salesforce/cli-plugins-testkit';
20+
import { expect } from 'chai';
21+
import { type Browser, type Page } from 'playwright';
22+
import { getSession } from '../helpers/sessionUtils.js';
23+
import { startLightningDevServer, getPreviewURL, killServerProcess } from '../helpers/devServerUtils.js';
24+
import { getPreview } from '../helpers/browserUtils.js';
25+
26+
const COMPONENT_NAME = 'helloWorld';
27+
const INITIAL_GREETING = 'Hello World';
28+
const STATIC_CONTENT = 'Static Content';
29+
30+
describe('lightning preview menu', () => {
31+
let session: TestSession;
32+
let childProcess: ChildProcessByStdio<Writable, Readable, Readable> | undefined;
33+
let browser: Browser;
34+
let page: Page;
35+
36+
beforeEach(async () => {
37+
session = await getSession();
38+
childProcess = startLightningDevServer(session, { AUTO_ENABLE_LOCAL_DEV: 'true' }, COMPONENT_NAME);
39+
const previewUrl = await getPreviewURL(childProcess.stdout);
40+
({ browser, page } = await getPreview(previewUrl, session));
41+
});
42+
43+
afterEach(async () => {
44+
if (page) await page.close();
45+
if (browser) await browser.close();
46+
killServerProcess(childProcess);
47+
});
48+
49+
it('should render select link and hamburger menu with helloWorld available and clickable', async () => {
50+
const greetingLocator = page.getByText(INITIAL_GREETING);
51+
await greetingLocator.waitFor({ state: 'visible' });
52+
53+
// When a component is already selected (e.g. --name helloWorld), the canvas shows the component,
54+
// not the "Select a component..." link. Open the hamburger to verify the panel and helloWorld.
55+
const menuToggle = page.getByRole('link', { name: 'Toggle menu' });
56+
await menuToggle.waitFor({ state: 'visible' });
57+
await menuToggle.scrollIntoViewIfNeeded();
58+
await menuToggle.click({ force: true });
59+
60+
// Hamburger opens lwr_dev-component-panel (slide-in panel)
61+
const componentPanel = page.locator('lwr_dev-component-panel >> .lwr-dev-component-panel__panel--visible');
62+
await componentPanel.waitFor({ state: 'visible' });
63+
64+
const staticItem = page.locator(
65+
'lwr_dev-component-panel >> .lwr-dev-component-panel__item[data-specifier="c/static"]',
66+
);
67+
await staticItem.waitFor({ state: 'visible' });
68+
await staticItem.click();
69+
70+
// Wait for the app to load the selected component (URL updates with specifier)
71+
await page.waitForURL(/specifier=c%2Fstatic|c\/static/, { timeout: 15_000 });
72+
73+
const staticContentLocator = page.getByText(STATIC_CONTENT);
74+
await staticContentLocator.waitFor({ state: 'visible', timeout: 15_000 });
75+
expect(await staticContentLocator.textContent()).to.include(STATIC_CONTENT);
76+
});
77+
78+
it('should render component in performance mode when performance mode button is clicked', async () => {
79+
const greetingLocator = page.getByText(INITIAL_GREETING);
80+
await greetingLocator.waitFor({ state: 'visible' });
81+
82+
const performanceLink = page.locator(
83+
'lwr_dev-preview-application >> lwr_dev-preview-header >> .lwr-dev-preview-header__performance-mode-link',
84+
);
85+
await performanceLink.waitFor({ state: 'visible' });
86+
await performanceLink.click();
87+
88+
await page.waitForURL(/mode=performance/);
89+
expect(page.url()).to.include('mode=performance');
90+
91+
const header = page.locator(
92+
'lwr_dev-preview-application >> lwr_dev-preview-header >> .lwr-dev-preview-header__header',
93+
);
94+
expect(await header.first().isHidden()).to.be.true;
95+
96+
const performanceLinkAfter = page.locator(
97+
'lwr_dev-preview-application >> lwr_dev-preview-header >> .lwr-dev-preview-header__performance-mode-link',
98+
);
99+
expect(await performanceLinkAfter.first().isHidden()).to.be.true;
100+
101+
await greetingLocator.waitFor({ state: 'visible' });
102+
expect(await greetingLocator.textContent()).to.equal(INITIAL_GREETING);
103+
});
104+
});

0 commit comments

Comments
 (0)