@@ -23,13 +23,16 @@ import {
2323 createEmptyWebappsDir ,
2424 createWebappDirWithoutMeta ,
2525 writeManifest ,
26+ webappPath ,
2627 ensureSfCli ,
2728 authOrgViaUrl ,
2829} from './helpers/webappProjectUtils.js' ;
2930
3031/* ------------------------------------------------------------------ *
3132 * Tier 1 — No Auth *
32- * Uses devhubAuthStrategy: 'NONE'. Always runs (CI + local). *
33+ * *
34+ * Validates flag-level parse errors that fire before any org or *
35+ * filesystem interaction. No credentials needed; always runs. *
3336 * ------------------------------------------------------------------ */
3437describe ( 'webapp dev NUTs — Tier 1 (no auth)' , ( ) => {
3538 let session : TestSession ;
@@ -42,6 +45,8 @@ describe('webapp dev NUTs — Tier 1 (no auth)', () => {
4245 await session ?. clean ( ) ;
4346 } ) ;
4447
48+ // --target-org is declared as Flags.requiredOrg(). Running without it
49+ // must fail at parse time with NoDefaultEnvError before any other logic.
4550 it ( 'should require --target-org' , ( ) => {
4651 const result = execCmd ( 'webapp dev --json' , {
4752 ensureExitCode : 1 ,
@@ -55,9 +60,12 @@ describe('webapp dev NUTs — Tier 1 (no auth)', () => {
5560
5661/* ------------------------------------------------------------------ *
5762 * Tier 2 — CLI Validation (with auth) *
58- * Requires TESTKIT_AUTH_URL or JWT credentials. *
59- * Skips gracefully when no credentials are present. *
60- * These tests exercise local validation only — no live org calls. *
63+ * *
64+ * Validates webapp discovery errors and URL resolution errors. *
65+ * Auth is only needed so --target-org passes parsing; these tests *
66+ * exercise local filesystem/network checks — no live org calls. *
67+ * *
68+ * Requires TESTKIT_AUTH_URL. Skips gracefully when absent. *
6169 * ------------------------------------------------------------------ */
6270describe ( 'webapp dev NUTs — Tier 2 CLI validation' , ( ) => {
6371 let session : TestSession ;
@@ -80,6 +88,7 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
8088
8189 // ── Discovery errors ──────────────────────────────────────────
8290
91+ // Project has no webapplications folder at all → WebappNotFoundError.
8392 it ( 'should error when no webapp found (project only, no webapps)' , ( ) => {
8493 const projectDir = createProject ( session , 'noWebappProject' ) ;
8594
@@ -91,6 +100,7 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
91100 expect ( result . jsonOutput ?. name ) . to . equal ( 'WebappNotFoundError' ) ;
92101 } ) ;
93102
103+ // Project has webapp "realApp" but --name asks for "NonExistent" → WebappNameNotFoundError.
94104 it ( 'should error when --name does not match any webapp' , ( ) => {
95105 const projectDir = createProjectWithWebapp ( session , 'nameNotFound' , 'realApp' ) ;
96106
@@ -102,11 +112,13 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
102112 expect ( result . jsonOutput ?. name ) . to . equal ( 'WebappNameNotFoundError' ) ;
103113 } ) ;
104114
115+ // cwd is inside webapp "appA" but --name asks for "appB" → WebappNameConflictError.
116+ // Discovery treats this as ambiguous intent and rejects it.
105117 it ( 'should error on --name conflict when inside a different webapp' , ( ) => {
106118 const projectDir = createProjectWithWebapp ( session , 'nameConflict' , 'appA' ) ;
107119 execSync ( 'sf webapp generate --name appB' , { cwd : projectDir , stdio : 'pipe' } ) ;
108120
109- const cwdInsideAppA = ` ${ projectDir } /force-app/main/default/webapplications/ appA` ;
121+ const cwdInsideAppA = webappPath ( projectDir , ' appA' ) ;
110122
111123 const result = execCmd ( `webapp dev --name appB --target-org ${ targetOrg } --json` , {
112124 ensureExitCode : 1 ,
@@ -116,6 +128,7 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
116128 expect ( result . jsonOutput ?. name ) . to . equal ( 'WebappNameConflictError' ) ;
117129 } ) ;
118130
131+ // webapplications/ folder exists but is empty → WebappNotFoundError.
119132 it ( 'should error when webapplications folder is empty' , ( ) => {
120133 const projectDir = createProject ( session , 'emptyWebapps' ) ;
121134 createEmptyWebappsDir ( projectDir ) ;
@@ -128,6 +141,7 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
128141 expect ( result . jsonOutput ?. name ) . to . equal ( 'WebappNotFoundError' ) ;
129142 } ) ;
130143
144+ // webapplications/orphanApp/ exists but has no .webapplication-meta.xml → not a valid webapp.
131145 it ( 'should error when webapp dir has no .webapplication-meta.xml' , ( ) => {
132146 const projectDir = createProject ( session , 'noMeta' ) ;
133147 createWebappDirWithoutMeta ( projectDir , 'orphanApp' ) ;
@@ -140,8 +154,35 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
140154 expect ( result . jsonOutput ?. name ) . to . equal ( 'WebappNotFoundError' ) ;
141155 } ) ;
142156
157+ // ── Auto-selection ────────────────────────────────────────────
158+
159+ // When cwd is inside webapplications/myApp/, discovery auto-selects that
160+ // webapp without --name. The command proceeds past discovery and fails at
161+ // URL resolution (no dev server running) — confirming auto-select worked.
162+ it ( 'should auto-select webapp when run from inside its directory' , ( ) => {
163+ const projectDir = createProjectWithWebapp ( session , 'autoSelect' , 'myApp' ) ;
164+
165+ writeManifest ( projectDir , 'myApp' , {
166+ dev : { url : 'http://localhost:5179' } ,
167+ } ) ;
168+
169+ const cwdInsideApp = webappPath ( projectDir , 'myApp' ) ;
170+
171+ // No --name flag; cwd is inside the webapp directory.
172+ // Discovery auto-selects myApp, then the command fails at URL check
173+ // (nothing running on 5179). DevServerUrlError proves discovery succeeded.
174+ const result = execCmd ( `webapp dev --target-org ${ targetOrg } --json` , {
175+ ensureExitCode : 1 ,
176+ cwd : cwdInsideApp ,
177+ } ) ;
178+
179+ expect ( result . jsonOutput ?. name ) . to . equal ( 'DevServerUrlError' ) ;
180+ } ) ;
181+
143182 // ── URL / dev server errors ───────────────────────────────────
144183
184+ // --url explicitly provided but nothing is listening → DevServerUrlError.
185+ // The command refuses to start a dev server when --url is given.
145186 it ( 'should error when --url is unreachable' , ( ) => {
146187 const projectDir = createProjectWithWebapp ( session , 'urlUnreachable' , 'myApp' ) ;
147188
@@ -153,17 +194,13 @@ describe('webapp dev NUTs — Tier 2 CLI validation', () => {
153194 expect ( result . jsonOutput ?. name ) . to . equal ( 'DevServerUrlError' ) ;
154195 } ) ;
155196
197+ // Manifest has dev.url but no dev.command → command can't start the server
198+ // itself and the URL is unreachable → DevServerUrlError.
156199 it ( 'should error when dev.url is unreachable and no dev.command' , ( ) => {
157200 const projectDir = createProjectWithWebapp ( session , 'urlNoCmd' , 'myApp' ) ;
158201
159202 writeManifest ( projectDir , 'myApp' , {
160- name : 'myApp' ,
161- label : 'My App' ,
162- version : '1.0.0' ,
163- outputDir : 'dist' ,
164- dev : {
165- url : 'http://localhost:5179' ,
166- } ,
203+ dev : { url : 'http://localhost:5179' } ,
167204 } ) ;
168205
169206 const result = execCmd ( `webapp dev --name myApp --target-org ${ targetOrg } --json` , {
0 commit comments