Skip to content

Commit 28803c1

Browse files
feat: e2e dev preview tests
1 parent d1b6aa6 commit 28803c1

32 files changed

Lines changed: 1404 additions & 644 deletions

.github/workflows/test.yml

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,76 @@ jobs:
3030
uses: salesforcecli/github-workflows/.github/workflows/unitTestsWindows.yml@main
3131
nuts:
3232
needs: linux-unit-tests
33-
uses: salesforcecli/github-workflows/.github/workflows/nut.yml@main
34-
secrets: inherit
33+
name: nuts (${{ matrix.os }}) / yarn test:nuts
34+
runs-on: ${{ matrix.os }}
3535
strategy:
3636
matrix:
3737
os: [ubuntu-latest, windows-latest]
3838
fail-fast: false
39-
with:
40-
os: ${{ matrix.os }}
39+
steps:
40+
- name: Configure git longpaths if on Windows
41+
if: runner.os == 'Windows'
42+
run: git config --system core.longpaths true
43+
44+
- uses: actions/checkout@v4
45+
46+
- uses: google/wireit@setup-github-actions-caching/v2
47+
continue-on-error: true
48+
49+
- uses: actions/setup-node@v4
50+
with:
51+
node-version: lts/*
52+
cache: yarn
53+
54+
- name: Cache node modules
55+
id: cache-nodemodules
56+
uses: actions/cache@v4
57+
env:
58+
cache-name: cache-node-modules
59+
with:
60+
path: '**/node_modules'
61+
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
62+
63+
- name: add CLI as global dependency
64+
uses: salesforcecli/github-workflows/.github/actions/retry@main
65+
with:
66+
max_attempts: 3
67+
command: npm install @salesforce/cli@nightly -g
68+
69+
- uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main
70+
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
71+
72+
- name: Install wireit
73+
run: yarn add wireit@^0.14.12
74+
75+
- run: yarn compile
76+
77+
- name: Install Playwright browsers
78+
run: yarn playwright install --with-deps
79+
80+
- name: Check that oclif config exists
81+
id: is-oclif-plugin
82+
run: echo "bool=$(jq 'if .oclif then true else false end' package.json)" >> "$GITHUB_OUTPUT"
83+
84+
- run: yarn oclif manifest
85+
if: steps.is-oclif-plugin.outputs.bool == 'true'
86+
87+
- name: NUTs with 5 attempts
88+
uses: salesforcecli/github-workflows/.github/actions/retry@main
89+
with:
90+
max_attempts: 5
91+
command: yarn test:nuts
92+
retry_on: error
93+
env:
94+
TESTKIT_AUTH_URL: ${{ secrets.TESTKIT_AUTH_URL }}
95+
TESTKIT_HUB_USERNAME: ${{ secrets.TESTKIT_HUB_USERNAME }}
96+
TESTKIT_JWT_CLIENT_ID: ${{ secrets.TESTKIT_JWT_CLIENT_ID }}
97+
TESTKIT_JWT_KEY: ${{ secrets.TESTKIT_JWT_KEY }}
98+
TESTKIT_HUB_INSTANCE: ${{ secrets.TESTKIT_HUB_INSTANCE }}
99+
ONEGP_TESTKIT_AUTH_URL: ${{ secrets.ONEGP_TESTKIT_AUTH_URL }}
100+
SF_CHANGE_CASE_SFDX_AUTH_URL: ${{ secrets.SF_CHANGE_CASE_SFDX_AUTH_URL }}
101+
SF_CHANGE_CASE_TEMPLATE_ID: ${{ secrets.SF_CHANGE_CASE_TEMPLATE_ID }}
102+
SF_CHANGE_CASE_CONFIGURATION_ITEM: ${{ secrets.SF_CHANGE_CASE_CONFIGURATION_ITEM }}
103+
TESTKIT_SETUP_RETRIES: 2
104+
SF_DISABLE_TELEMETRY: true
105+
DEBUG: ${{ vars.DEBUG }}

.github/workflows/validate-pr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
branches:
88
- main
99
- '2*-patch'
10+
workflow_dispatch:
1011

1112
jobs:
1213
check-versions:

.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
}

.vscode/tasks.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@
44
"tasks": [
55
{
66
"label": "Build CLI Plugin",
7-
"group": {
8-
"kind": "build",
9-
"isDefault": true
10-
},
7+
"group": { "kind": "build", "isDefault": true },
118
"command": "yarn",
129
"type": "shell",
13-
"presentation": {
14-
"focus": false,
15-
"panel": "dedicated"
16-
},
10+
"presentation": { "focus": false, "panel": "dedicated" },
1711
"args": ["build"],
1812
"isBackground": false
13+
},
14+
{
15+
"label": "Compile plugin only",
16+
"command": "yarn",
17+
"type": "shell",
18+
"presentation": { "focus": false, "panel": "shared" },
19+
"args": ["compile"],
20+
"isBackground": false,
21+
"problemMatcher": "$tsc"
1922
}
2023
]
2124
}

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
## [6.2.12](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.11...6.2.12) (2026-02-22)
2+
3+
### Bug Fixes
4+
5+
- **deps:** bump @lwrjs/api from 0.21.5 to 0.21.8 ([#630](https://github.com/salesforcecli/plugin-lightning-dev/issues/630)) ([bd46490](https://github.com/salesforcecli/plugin-lightning-dev/commit/bd46490bb724bee54b235b0474abe824aaf6935e))
6+
7+
## [6.2.11](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.10...6.2.11) (2026-02-22)
8+
9+
### Bug Fixes
10+
11+
- **deps:** bump @salesforce/core from 8.25.1 to 8.26.2 ([#632](https://github.com/salesforcecli/plugin-lightning-dev/issues/632)) ([da0b067](https://github.com/salesforcecli/plugin-lightning-dev/commit/da0b0678f3d924c354dab9d8e88c443cbff2003a))
12+
13+
## [6.2.10](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.9...6.2.10) (2026-02-21)
14+
15+
### Bug Fixes
16+
17+
- **deps:** bump glob from 13.0.5 to 13.0.6 ([#628](https://github.com/salesforcecli/plugin-lightning-dev/issues/628)) ([8af0831](https://github.com/salesforcecli/plugin-lightning-dev/commit/8af0831e3aa2152e84dbe8b6f5581a2845495923))
18+
19+
## [6.2.9](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.8...6.2.9) (2026-02-18)
20+
21+
### Bug Fixes
22+
23+
- **deps:** bump glob from 13.0.4 to 13.0.5 ([#625](https://github.com/salesforcecli/plugin-lightning-dev/issues/625)) ([7a3c708](https://github.com/salesforcecli/plugin-lightning-dev/commit/7a3c7088ce925c6d4b68ee1e9a1d9e440bf6ee81))
24+
125
## [6.2.8](https://github.com/salesforcecli/plugin-lightning-dev/compare/6.2.7...6.2.8) (2026-02-18)
226

327
### Bug Fixes

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ EXAMPLES
203203
$ sf lightning dev app --target-org myOrg --device-type ios --device-id "iPhone 15 Pro Max"
204204
```
205205

206-
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.8/src/commands/lightning/dev/app.ts)_
206+
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.12/src/commands/lightning/dev/app.ts)_
207207

208208
## `sf lightning dev component`
209209

@@ -251,7 +251,12 @@ EXAMPLES
251251
$ sf lightning dev component --name myComponent
252252
```
253253

254-
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.8/src/commands/lightning/dev/component.ts)_
254+
<<<<<<< HEAD
255+
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.9/src/commands/lightning/dev/component.ts)_
256+
=======
257+
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.12/src/commands/lightning/dev/component.ts)_
258+
259+
> > > > > > > main
255260
256261
## `sf lightning dev site`
257262

@@ -308,6 +313,11 @@ EXAMPLES
308313
$ sf lightning dev site --name "Partner Central" --target-org myOrg --get-latest
309314
```
310315

311-
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.8/src/commands/lightning/dev/site.ts)_
316+
<<<<<<< HEAD
317+
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.9/src/commands/lightning/dev/site.ts)_
318+
=======
319+
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/6.2.12/src/commands/lightning/dev/site.ts)_
320+
321+
> > > > > > > main
312322
313323
<!-- commandsstop -->

package.json

Lines changed: 10 additions & 6 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.8",
4+
"version": "6.2.12",
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
@@ -10,14 +10,14 @@
1010
"@lwc/sfdx-local-dev-dist": "~13.3.19",
1111
"@lwc/sfdx-local-dev-dist-66.0": "npm:@lwc/sfdx-local-dev-dist@~13.2.24-alpha.0",
1212
"@lwc/sfdx-local-dev-dist-67.0": "npm:@lwc/sfdx-local-dev-dist@~13.3.19",
13-
"@lwrjs/api": "0.21.5",
13+
"@lwrjs/api": "0.21.8",
1414
"@oclif/core": "^4.5.6",
15-
"@salesforce/core": "^8.25.1",
15+
"@salesforce/core": "^8.26.2",
1616
"@salesforce/kit": "^3.2.4",
1717
"@salesforce/lwc-dev-mobile-core": "4.0.0-alpha.14",
1818
"@salesforce/sf-plugins-core": "^11.2.4",
1919
"axios": "^1.13.5",
20-
"glob": "^13.0.4",
20+
"glob": "^13.0.6",
2121
"lwc": "~8.28.2",
2222
"node-fetch": "^3.3.2",
2323
"open": "^10.2.0",
@@ -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",
@@ -39,7 +41,7 @@
3941
"eslint-plugin-sf-plugin": "^1.20.33",
4042
"eslint-plugin-unicorn": "^50.0.1",
4143
"esmock": "^2.7.3",
42-
"oclif": "^4.22.77",
44+
"oclif": "^4.22.80",
4345
"ts-node": "^10.9.2",
4446
"typescript": "^5.5.4"
4547
},
@@ -103,7 +105,9 @@
103105
"prepack": "sf-prepack",
104106
"prepare": "sf-install",
105107
"test": "wireit",
106-
"test:nuts": "nyc mocha \"**/*.nut.ts\" --slow 4500 --timeout 600000 --parallel",
108+
"test:nuts": "mocha \"**/*.nut.ts\" --slow 30000 --timeout 600000 --parallel=false",
109+
"test:nuts:local": "node -r dotenv/config ./node_modules/.bin/nyc mocha \"**/*.nut.ts\" --slow 30000 --timeout 600000 --parallel=false",
110+
"test:nut:local": "node -r dotenv/config ./node_modules/.bin/nyc mocha --slow 30000 --timeout 600000",
107111
"test:only": "wireit",
108112
"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",
109113
"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
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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(
39+
session.project?.dir ?? '',
40+
session.hubOrg.username,
41+
{ AUTO_ENABLE_LOCAL_DEV: 'true' },
42+
COMPONENT_NAME,
43+
);
44+
const previewUrl = await getPreviewURL(childProcess.stdout);
45+
({ browser, page } = await getPreview(previewUrl, session.hubOrg.accessToken));
46+
});
47+
48+
afterEach(async () => {
49+
if (page) await page.close();
50+
if (browser) await browser.close();
51+
killServerProcess(childProcess);
52+
});
53+
54+
it('should render select link and hamburger menu with helloWorld available and clickable', async () => {
55+
const greetingLocator = page.getByText(INITIAL_GREETING);
56+
await greetingLocator.waitFor({ state: 'visible' });
57+
58+
// When a component is already selected (e.g. --name helloWorld), the canvas shows the component,
59+
// not the "Select a component..." link. Open the hamburger to verify the panel and helloWorld.
60+
const menuToggle = page.getByRole('link', { name: 'Toggle menu' });
61+
await menuToggle.waitFor({ state: 'visible' });
62+
await menuToggle.scrollIntoViewIfNeeded();
63+
await menuToggle.click({ force: true });
64+
65+
// Hamburger opens lwr_dev-component-panel (slide-in panel)
66+
const componentPanel = page.locator('lwr_dev-component-panel >> .lwr-dev-component-panel__panel--visible');
67+
await componentPanel.waitFor({ state: 'visible' });
68+
69+
const staticItem = page.locator(
70+
'lwr_dev-component-panel >> .lwr-dev-component-panel__item[data-specifier="c/static"]',
71+
);
72+
await staticItem.waitFor({ state: 'visible' });
73+
await staticItem.click();
74+
75+
// Wait for the app to load the selected component (URL updates with specifier)
76+
await page.waitForURL(/specifier=c%2Fstatic|c\/static/, { timeout: 15_000 });
77+
78+
const staticContentLocator = page.getByText(STATIC_CONTENT);
79+
await staticContentLocator.waitFor({ state: 'visible', timeout: 15_000 });
80+
expect(await staticContentLocator.textContent()).to.include(STATIC_CONTENT);
81+
});
82+
83+
it('should render component in performance mode when performance mode button is clicked', async () => {
84+
const greetingLocator = page.getByText(INITIAL_GREETING);
85+
await greetingLocator.waitFor({ state: 'visible' });
86+
87+
const performanceLink = page.locator(
88+
'lwr_dev-preview-application >> lwr_dev-preview-header >> .lwr-dev-preview-header__performance-mode-link',
89+
);
90+
await performanceLink.waitFor({ state: 'visible' });
91+
await performanceLink.click();
92+
93+
await page.waitForURL(/mode=performance/);
94+
expect(page.url()).to.include('mode=performance');
95+
96+
const header = page.locator(
97+
'lwr_dev-preview-application >> lwr_dev-preview-header >> .lwr-dev-preview-header__header',
98+
);
99+
expect(await header.first().isHidden()).to.be.true;
100+
101+
const performanceLinkAfter = page.locator(
102+
'lwr_dev-preview-application >> lwr_dev-preview-header >> .lwr-dev-preview-header__performance-mode-link',
103+
);
104+
expect(await performanceLinkAfter.first().isHidden()).to.be.true;
105+
106+
await greetingLocator.waitFor({ state: 'visible' });
107+
expect(await greetingLocator.textContent()).to.equal(INITIAL_GREETING);
108+
});
109+
});

0 commit comments

Comments
 (0)