Skip to content

Commit d2ca6d3

Browse files
authored
fix: enhance PathReplacer to support workspaceFolder for path resolution (#413)
- Updated ProcessBuilderFactory to inject WorkspaceFolder. - Modified PathReplacer to utilize workspaceFolder for variable replacement. - Added tests to verify behavior when workspaceFolder differs from cwd in nested configurations.
1 parent 0d8571c commit d2ca6d3

3 files changed

Lines changed: 74 additions & 6 deletions

File tree

packages/extension/src/TestExecution/ProcessBuilderFactory.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,25 @@ import {
77
Xdebug,
88
} from '@vscode-phpunit/phpunit';
99
import { inject, injectable } from 'inversify';
10-
import { TestRunProfileKind } from 'vscode';
10+
import { TestRunProfileKind, type WorkspaceFolder } from 'vscode';
1111
import { Configuration } from '../Configuration';
12+
import { TYPES } from '../types';
1213

1314
@injectable()
1415
export class ProcessBuilderFactory {
1516
constructor(
1617
@inject(Configuration) private config: Configuration,
1718
@inject(PHPUnitXML) private phpUnitXML: PHPUnitXML,
19+
@inject(TYPES.WorkspaceFolder) private workspaceFolder: WorkspaceFolder,
1820
) {}
1921

2022
async create(profileKind?: TestRunProfileKind): Promise<ProcessBuilder> {
2123
const options = { cwd: this.phpUnitXML.root() };
22-
const pathReplacer = new PathReplacer(options, this.config.get('paths') as Path);
24+
const pathReplacer = new PathReplacer(
25+
options,
26+
this.config.get('paths') as Path,
27+
this.workspaceFolder.uri.fsPath,
28+
);
2329
const xdebug = await new Xdebug(this.config).setMode(this.toMode(profileKind));
2430
return new ProcessBuilder(this.config, options, pathReplacer, xdebug);
2531
}

packages/phpunit/src/Configuration/PathReplacer.test.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,33 @@ import {
1212
import { PathReplacer } from './PathReplacer';
1313

1414
describe('PathReplacer', () => {
15-
const givenPathReplacer = (paths?: Record<string, string>, cwd?: string) => {
15+
const givenPathReplacer = (
16+
paths?: Record<string, string>,
17+
cwd?: string,
18+
workspaceFolder?: string,
19+
) => {
1620
return new PathReplacer(
1721
{ cwd: cwd ?? phpUnitProject('') },
1822
paths ?? {
1923
[VAR_WORKSPACE_FOLDER]: '/app',
2024
},
25+
workspaceFolder,
2126
);
2227
};
2328

2429
const toWindows = (path: string) => path.replace(/\//g, '\\').replace(/\\$/g, '');
2530

26-
const givenPathReplacerForWindows = (paths?: Record<string, string>, cwd?: string) => {
31+
const givenPathReplacerForWindows = (
32+
paths?: Record<string, string>,
33+
cwd?: string,
34+
workspaceFolder?: string,
35+
) => {
2736
return new PathReplacer(
2837
{ cwd: cwd ?? phpUnitProjectWin('') },
2938
paths ?? {
3039
[VAR_WORKSPACE_FOLDER]: '/app',
3140
},
41+
workspaceFolder,
3242
);
3343
};
3444

@@ -282,4 +292,52 @@ describe('PathReplacer', () => {
282292
const remotePath = '/app/tests/AssertionsTest.php';
283293
expect(pathReplacer.toLocal(remotePath)).toEqual(remotePath);
284294
});
295+
296+
describe('workspaceFolder differs from cwd (monorepo / nested config)', () => {
297+
const workspace = '/workspace';
298+
const cwd = '/workspace/apps/api';
299+
300+
it(`replaces ${VAR_WORKSPACE_FOLDER} with workspaceFolder, not cwd`, () => {
301+
const pathReplacer = new PathReplacer({ cwd }, undefined, workspace);
302+
303+
expect(
304+
pathReplacer.toRemote(`${VAR_WORKSPACE_FOLDER}/apps/api/phpunit.xml.dist`),
305+
).toEqual('/workspace/apps/api/phpunit.xml.dist');
306+
});
307+
308+
it(`replaces ${VAR_WORKSPACE_FOLDER_BASENAME} with basename of workspaceFolder, not cwd`, () => {
309+
const pathReplacer = new PathReplacer({ cwd }, undefined, workspace);
310+
311+
expect(pathReplacer.replacePathVariables(`${VAR_WORKSPACE_FOLDER_BASENAME}`)).toEqual(
312+
'workspace',
313+
);
314+
});
315+
316+
it(`replaces ${VAR_PWD} with cwd, not workspaceFolder`, () => {
317+
const pathReplacer = new PathReplacer({ cwd }, undefined, workspace);
318+
319+
expect(pathReplacer.toRemote(`${VAR_PWD}/vendor/bin/phpunit`)).toEqual(
320+
'/workspace/apps/api/vendor/bin/phpunit',
321+
);
322+
});
323+
324+
it(`does not double-nest path when ${VAR_WORKSPACE_FOLDER} contains subdirectory`, () => {
325+
const pathReplacer = new PathReplacer({ cwd }, undefined, workspace);
326+
327+
const result = pathReplacer.toRemote(
328+
`${VAR_WORKSPACE_FOLDER}/apps/api/vendor/bin/phpunit`,
329+
);
330+
331+
expect(result).toEqual('/workspace/apps/api/vendor/bin/phpunit');
332+
expect(result).not.toContain('apps/api/apps/api');
333+
});
334+
335+
it(`falls back to cwd when workspaceFolder is not provided`, () => {
336+
const pathReplacer = new PathReplacer({ cwd });
337+
338+
expect(
339+
pathReplacer.toRemote(`${VAR_WORKSPACE_FOLDER}/apps/api/phpunit.xml.dist`),
340+
).toEqual('/workspace/apps/api/apps/api/phpunit.xml.dist');
341+
});
342+
});
285343
});

packages/phpunit/src/Configuration/PathReplacer.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,20 @@ export class PathReplacer {
2929
constructor(
3030
private options: SpawnOptions = {},
3131
paths?: Path,
32+
workspaceFolder?: string,
3233
) {
3334
this.cwd = this.fixWindowsDriveLetter(
3435
(this.options?.cwd as string) ?? (process.env.cwd as string),
3536
);
37+
const workspaceFolderPath = workspaceFolder
38+
? this.fixWindowsDriveLetter(workspaceFolder)
39+
: this.cwd;
3640
this.pathVariables = new Map<string, string>();
3741
this.pathVariables.set(VAR_PWD, this.cwd);
38-
this.pathVariables.set(VAR_WORKSPACE_FOLDER, this.cwd);
42+
this.pathVariables.set(VAR_WORKSPACE_FOLDER, workspaceFolderPath);
3943
this.pathVariables.set(
4044
VAR_WORKSPACE_FOLDER_BASENAME,
41-
this.cwd ? path.basename(this.cwd) : '',
45+
workspaceFolderPath ? path.basename(workspaceFolderPath) : '',
4246
);
4347
this.pathVariables.set(VAR_USER_HOME, os.homedir());
4448
this.pathVariables.set(VAR_PATH_SEPARATOR, path.sep);

0 commit comments

Comments
 (0)