Path: blob/main/src/vs/workbench/services/dialogs/test/browser/simpleFileDialog.test.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { URI } from '../../../../../base/common/uri.js';7import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';8import { AGENT_HOST_SCHEME, AGENT_HOST_LABEL_FORMATTER, agentHostAuthority } from '../../../../../platform/agentHost/common/agentHostUri.js';9import { agentHostUri } from '../../../../../platform/agentHost/common/agentHostFileSystemProvider.js';1011/**12* Tests for the scoped path prefix logic used by SimpleFileDialog.13*14* SimpleFileDialog is tightly coupled to many services and difficult to15* instantiate in isolation. Instead of mocking the full dialog, we test16* the underlying data transformations that drive the fix:17*18* 1. computeScopedPathPrefix - derived from comparing the raw URI path19* with the label-service-formatted output.20* 2. pathFromUri - stripping the prefix from the raw path.21* 3. remoteUriFrom - re-adding the prefix to user input.22*/23suite('SimpleFileDialog - scoped path prefix', () => {2425ensureNoDisposablesAreLeakedInTestSuite();2627/**28* Replicates the stripPathSegments logic from the label service to29* produce the display path that the label formatter would return.30*/31function labelFormatterDisplay(path: string, stripSegments: number): string {32let pos = 0;33for (let i = 0; i < stripSegments; i++) {34const next = path.indexOf('/', pos + 1);35if (next === -1) {36break;37}38pos = next;39}40return path.substring(pos);41}4243/**44* Replicates SimpleFileDialog.computeScopedPathPrefix:45* compares raw URI path with formatted display path to find the prefix.46*/47function computeScopedPathPrefix(uri: URI, displayPath: string): string {48const fullPath = uri.path;49if (displayPath && fullPath.endsWith(displayPath)) {50return fullPath.substring(0, fullPath.length - displayPath.length);51}52return '';53}5455/**56* Replicates the scoped branch of SimpleFileDialog.pathFromUri:57* strips the prefix from the raw URI path.58*/59function pathFromUri(uri: URI, prefix: string, endWithSeparator: boolean = false): string {60let path = uri.path;61if (prefix && path.startsWith(prefix)) {62path = path.substring(prefix.length);63}64let result = path.replace(/\n/g, '');65result = result.replace(/\\/g, '/');66if (endWithSeparator && !result.endsWith('/')) {67result = result + '/';68}69return result;70}7172/**73* Replicates the scoped branch of SimpleFileDialog.remoteUriFrom:74* re-adds the prefix to construct a proper URI.75*/76function remoteUriFrom(path: string, scheme: string, authority: string, prefix: string): URI {77return URI.from({ scheme, authority, path: prefix + path });78}7980test('computeScopedPathPrefix extracts prefix for agent host URI', () => {81const authority = agentHostAuthority('localhost:8089');82const uri = agentHostUri(authority, '/Users/roblou/code');8384const displayPath = labelFormatterDisplay(uri.path, AGENT_HOST_LABEL_FORMATTER.formatting.stripPathSegments!);85const prefix = computeScopedPathPrefix(uri, displayPath);8687assert.strictEqual(prefix, '/file/-');88assert.strictEqual(displayPath, '/Users/roblou/code');89});9091test('computeScopedPathPrefix works for URI with original authority', () => {92const authority = agentHostAuthority('localhost:8089');93const originalUri = URI.from({ scheme: 'agenthost-content', authority: 'session1', path: '/snap/before' });94const uri = URI.from({95scheme: AGENT_HOST_SCHEME,96authority,97path: `/${originalUri.scheme}/${originalUri.authority}${originalUri.path}`,98});99100const displayPath = labelFormatterDisplay(uri.path, AGENT_HOST_LABEL_FORMATTER.formatting.stripPathSegments!);101const prefix = computeScopedPathPrefix(uri, displayPath);102103assert.strictEqual(prefix, '/agenthost-content/session1');104assert.strictEqual(displayPath, '/snap/before');105});106107test('computeScopedPathPrefix returns empty for scheme without stripping', () => {108const uri = URI.from({ scheme: 'file', path: '/Users/roblou/code' });109// If display matches the full path, prefix is empty110const prefix = computeScopedPathPrefix(uri, '/Users/roblou/code');111assert.strictEqual(prefix, '');112});113114test('pathFromUri strips prefix to show clean path', () => {115const authority = agentHostAuthority('localhost:8089');116const uri = agentHostUri(authority, '/Users/roblou/code');117const prefix = '/file/-';118119assert.strictEqual(pathFromUri(uri, prefix), '/Users/roblou/code');120});121122test('pathFromUri with trailing separator', () => {123const authority = agentHostAuthority('localhost:8089');124const uri = agentHostUri(authority, '/Users/roblou/code');125const prefix = '/file/-';126127assert.strictEqual(pathFromUri(uri, prefix, true), '/Users/roblou/code/');128});129130test('pathFromUri without prefix returns raw path', () => {131const uri = URI.from({ scheme: 'file', path: '/Users/roblou/code' });132assert.strictEqual(pathFromUri(uri, ''), '/Users/roblou/code');133});134135test('remoteUriFrom re-adds prefix to reconstruct encoded URI', () => {136const authority = agentHostAuthority('localhost:8089');137const prefix = '/file/-';138const cleanPath = '/Users/roblou/code';139140const result = remoteUriFrom(cleanPath, AGENT_HOST_SCHEME, authority, prefix);141142assert.strictEqual(result.scheme, AGENT_HOST_SCHEME);143assert.strictEqual(result.authority, authority);144assert.strictEqual(result.path, '/file/-/Users/roblou/code');145});146147test('full round-trip: URI -> pathFromUri -> remoteUriFrom -> same URI', () => {148const authority = agentHostAuthority('localhost:8089');149const originalPath = '/Users/roblou/code/vscode';150const uri = agentHostUri(authority, originalPath);151152// Compute prefix153const displayPath = labelFormatterDisplay(uri.path, AGENT_HOST_LABEL_FORMATTER.formatting.stripPathSegments!);154const prefix = computeScopedPathPrefix(uri, displayPath);155156// pathFromUri extracts clean path157const cleanPath = pathFromUri(uri, prefix);158assert.strictEqual(cleanPath, originalPath);159160// remoteUriFrom reconstructs the original URI161const reconstructed = remoteUriFrom(cleanPath, AGENT_HOST_SCHEME, authority, prefix);162assert.strictEqual(reconstructed.path, uri.path);163assert.strictEqual(reconstructed.scheme, uri.scheme);164assert.strictEqual(reconstructed.authority, uri.authority);165});166167test('createBackItem root detection with prefix', () => {168const authority = agentHostAuthority('localhost:8089');169const prefix = '/file/-';170171// Simulate root folder: path = prefix + '/'172const rootUri = URI.from({ scheme: AGENT_HOST_SCHEME, authority, path: prefix + '/' });173const pathAfterPrefix = rootUri.path.substring(prefix.length);174assert.strictEqual(pathAfterPrefix === '/' || pathAfterPrefix === '', true, 'root should be detected');175176// Simulate non-root folder177const subUri = URI.from({ scheme: AGENT_HOST_SCHEME, authority, path: prefix + '/Users/roblou' });178const subPathAfterPrefix = subUri.path.substring(prefix.length);179assert.notStrictEqual(subPathAfterPrefix, '/');180assert.notStrictEqual(subPathAfterPrefix, '');181});182});183184185