diff --git a/patches/disable-show-local.diff b/patches/disable-show-local.diff new file mode 100644 index 000000000000..84b2a318dc3f --- /dev/null +++ b/patches/disable-show-local.diff @@ -0,0 +1,137 @@ +Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts ++++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +@@ -17,6 +17,7 @@ export const serverOptions: OptionDescri + 'disable-update-check': { type: 'boolean' }, + 'auth': { type: 'string' }, + 'disable-file-downloads': { type: 'boolean' }, ++ 'disable-show-local': { type: 'boolean' }, + 'locale': { type: 'string' }, + 'disable-getting-started-override': { type: 'boolean' }, + +@@ -101,6 +102,7 @@ export interface ServerParsedArgs { + 'disable-update-check'?: boolean; + 'auth'?: string + 'disable-file-downloads'?: boolean; ++ 'disable-show-local'?: boolean; + 'locale'?: string + 'disable-getting-started-override'?: boolean, + +Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts ++++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts +@@ -335,6 +335,7 @@ export class WebClientServer { + webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', + userDataPath: this._environmentService.userDataPath, + isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], ++ isEnabledShowLocal: !this._environmentService.args['disable-show-local'], + isEnabledCoderGettingStarted: !this._environmentService.args['disable-getting-started-override'], + _wrapWebWorkerExtHostInIframe, + developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, +Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts ++++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +@@ -287,6 +287,11 @@ export interface IWorkbenchConstructionO + readonly isEnabledFileDownloads?: boolean + + /** ++ * Whether the "Show local" button is enabled. ++ */ ++ readonly isEnabledShowLocal?: boolean ++ ++ /** + * Whether to use Coder's custom Getting Started text. + */ + readonly isEnabledCoderGettingStarted?: boolean +Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts ++++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts +@@ -39,6 +39,11 @@ export interface IBrowserWorkbenchEnviro + readonly isEnabledFileDownloads?: boolean; + + /** ++ * Enable Show Local button on File Dialogs. ++ */ ++ readonly isEnabledShowLocal?: boolean; ++ ++ /** + * Enable Coder's custom getting started text. + */ + readonly isEnabledCoderGettingStarted?: boolean; +@@ -128,6 +133,13 @@ export class BrowserWorkbenchEnvironment + return this.options.isEnabledFileDownloads; + } + ++ get isEnabledShowLocal(): boolean { ++ if (typeof this.options.isEnabledShowLocal === "undefined") { ++ throw new Error('isEnabledShowLocal was not provided to the browser'); ++ } ++ return this.options.isEnabledShowLocal; ++ } ++ + get isEnabledCoderGettingStarted(): boolean { + if (typeof this.options.isEnabledCoderGettingStarted === "undefined") { + throw new Error('isEnabledCoderGettingStarted was not provided to the browser'); +Index: code-server/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts ++++ code-server/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +@@ -18,7 +18,7 @@ import { IModelService } from 'vs/editor + import { ILanguageService } from 'vs/editor/common/languages/language'; + import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; + import { Schemas } from 'vs/base/common/network'; +-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; ++import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; + import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; +@@ -142,7 +142,7 @@ export class SimpleFileDialog implements + @IFileDialogService private readonly fileDialogService: IFileDialogService, + @IModelService private readonly modelService: IModelService, + @ILanguageService private readonly languageService: ILanguageService, +- @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, ++ @IBrowserWorkbenchEnvironmentService protected readonly environmentService: IBrowserWorkbenchEnvironmentService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IPathService protected readonly pathService: IPathService, + @IKeybindingService private readonly keybindingService: IKeybindingService, +@@ -286,20 +286,22 @@ export class SimpleFileDialog implements + this.filePickBox.autoFocusOnList = false; + this.filePickBox.ignoreFocusOut = true; + this.filePickBox.ok = true; +- if ((this.scheme !== Schemas.file) && this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) { +- this.filePickBox.customButton = true; +- this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); +- let action; +- if (isSave) { +- action = SaveLocalFileCommand; +- } else { +- action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderCommand : OpenLocalFileCommand) : OpenLocalFolderCommand; +- } +- const keybinding = this.keybindingService.lookupKeybinding(action.ID); +- if (keybinding) { +- const label = keybinding.getLabel(); +- if (label) { +- this.filePickBox.customHover = format('{0} ({1})', action.LABEL, label); ++ if ((this.environmentService.isEnabledFileDownloads || !isSave) && this.environmentService.isEnabledShowLocal) { ++ if ((this.scheme !== Schemas.file) && this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) { ++ this.filePickBox.customButton = true; ++ this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); ++ let action; ++ if (isSave) { ++ action = SaveLocalFileCommand; ++ } else { ++ action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderCommand : OpenLocalFileCommand) : OpenLocalFolderCommand; ++ } ++ const keybinding = this.keybindingService.lookupKeybinding(action.ID); ++ if (keybinding) { ++ const label = keybinding.getLabel(); ++ if (label) { ++ this.filePickBox.customHover = format('{0} ({1})', action.LABEL, label); ++ } + } + } + } diff --git a/patches/series b/patches/series index 898d2974af05..df752110e519 100644 --- a/patches/series +++ b/patches/series @@ -19,3 +19,4 @@ display-language.diff cli-window-open.diff getting-started.diff safari.diff +disable-show-local.diff diff --git a/src/node/cli.ts b/src/node/cli.ts index ff80c88729c0..f448eae5e674 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -48,6 +48,7 @@ export interface UserProvidedCodeArgs { "github-auth"?: string "disable-update-check"?: boolean "disable-file-downloads"?: boolean + "disable-show-local"?: boolean "disable-workspace-trust"?: boolean "disable-getting-started-override"?: boolean "disable-proxy"?: boolean @@ -170,6 +171,11 @@ export const options: Options> = { description: "Disable file downloads from Code. This can also be set with CS_DISABLE_FILE_DOWNLOADS set to 'true' or '1'.", }, + "disable-show-local": { + type: "boolean", + description: + "Disable 'Show Local' button in Save and Open dialogs.", + }, "disable-workspace-trust": { type: "boolean", description: "Disable Workspace Trust feature. This switch only affects the current session.", diff --git a/test/e2e/downloads.test.ts b/test/e2e/downloads.test.ts index f155f2387f3b..bb533410da46 100644 --- a/test/e2e/downloads.test.ts +++ b/test/e2e/downloads.test.ts @@ -23,6 +23,26 @@ describe("Downloads (enabled)", ["--disable-workspace-trust"], {}, async () => { expect(await codeServerPage.page.isVisible("text=Download...")).toBe(true) }) + + test("should see the 'Show Local' button on 'Save As'", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const fileName = "unique-file-save-as.txt" + const tmpFilePath = path.join(workspaceDir, fileName) + await fs.writeFile(tmpFilePath, "Hello World") + + // Action + await codeServerPage.page.waitForSelector(`text=${fileName}`) + + await codeServerPage.openFile(fileName) + await codeServerPage.page.click(".tab") + await codeServerPage.navigateMenus(["File", "Auto Save"]) + await codeServerPage.page.keyboard.type("Making some edits.") + await codeServerPage.navigateMenus(["File", "Save As..."]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true) + }) + }) describe("Downloads (disabled)", ["--disable-workspace-trust", "--disable-file-downloads"], {}, async () => { @@ -35,7 +55,7 @@ describe("Downloads (disabled)", ["--disable-workspace-trust", "--disable-file-d // Setup const workspaceDir = await codeServerPage.workspaceDir const tmpFilePath = path.join(workspaceDir, "unique-file.txt") - await fs.writeFile(tmpFilePath, "hello world") + await fs.writeFile(tmpFilePath, "Hello World") // Action const fileInExplorer = await codeServerPage.page.waitForSelector("text=unique-file.txt") @@ -45,4 +65,20 @@ describe("Downloads (disabled)", ["--disable-workspace-trust", "--disable-file-d expect(await codeServerPage.page.isVisible("text=Download...")).toBe(false) }) + + test("should not see the 'Show Local' button on 'Save As'", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const fileName = "unique-file-save-as.txt" + const tmpFilePath = path.join(workspaceDir, fileName) + await fs.writeFile(tmpFilePath, "Hello World") + + // Action + await codeServerPage.page.waitForSelector(`text=${fileName}`) + await codeServerPage.openFile(fileName) + await codeServerPage.page.click(".tab") + await codeServerPage.navigateMenus(["File", "Save As..."]) + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false) + }) + }) diff --git a/test/e2e/models/CodeServer.ts b/test/e2e/models/CodeServer.ts index 27ce15459f16..76accbc4c24d 100644 --- a/test/e2e/models/CodeServer.ts +++ b/test/e2e/models/CodeServer.ts @@ -401,7 +401,7 @@ export class CodeServerPage { * Open a file by using menus. */ async openFile(file: string) { - await this.navigateMenus(["File", "Open File"]) + await this.navigateMenus(["File", "Open File..."]) await this.navigateQuickInput([path.basename(file)]) await this.waitForTab(file) } @@ -488,19 +488,19 @@ export class CodeServerPage { // splitting them into two steps each we can cancel before running the // action. steps.push({ - fn: () => this.page.hover(`${selector} :text("${item}")`, { trial: true }), + fn: () => this.page.hover(`${selector} :text-is("${item}")`, { trial: true }), name: `${item}:hover:trial`, }) steps.push({ - fn: () => this.page.hover(`${selector} :text("${item}")`, { force: true }), + fn: () => this.page.hover(`${selector} :text-is("${item}")`, { force: true }), name: `${item}:hover:force`, }) steps.push({ - fn: () => this.page.click(`${selector} :text("${item}")`, { trial: true }), + fn: () => this.page.click(`${selector} :text-is("${item}")`, { trial: true }), name: `${item}:click:trial`, }) steps.push({ - fn: () => this.page.click(`${selector} :text("${item}")`, { force: true }), + fn: () => this.page.click(`${selector} :text-is("${item}")`, { force: true }), name: `${item}:click:force`, }) } diff --git a/test/e2e/show-local.test.ts b/test/e2e/show-local.test.ts new file mode 100644 index 000000000000..9a2327eb2b65 --- /dev/null +++ b/test/e2e/show-local.test.ts @@ -0,0 +1,99 @@ +import { promises as fs } from "fs" +import * as path from "path" +import { clean } from "../utils/helpers" +import { describe, test, expect } from "./baseFixture" + +describe("Show Local (enabled)", ["--disable-workspace-trust"], {}, () => { + const testName = "show-local-enabled" + test.beforeAll(async () => { + await clean(testName) + }) + + test("should see the 'Show Local' button on Open File", async ({ codeServerPage }) => { + // Action + await codeServerPage.navigateMenus(["File", "Open File..."]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true) + }) + + test("should see the 'Show Local' button on Save as File", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const fileName = "unique-file-save-as.txt" + const tmpFilePath = path.join(workspaceDir, fileName) + await fs.writeFile(tmpFilePath, "Hello World") + + // Action + await codeServerPage.page.waitForSelector(`text=${fileName}`) + + await codeServerPage.openFile(fileName) + await codeServerPage.page.click(".tab") + await codeServerPage.navigateMenus(["File", "Auto Save"]) + await codeServerPage.page.keyboard.type("Edit edit edit.") + await codeServerPage.navigateMenus(["File", "Save As..."]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true) + }) + + test("should see the 'Show Local' button on Save File", async ({ codeServerPage }) => { + + // Action + await codeServerPage.navigateMenus(["File", "New Text File"]) + await codeServerPage.waitForTab("Untitled-1") + await codeServerPage.navigateMenus(["File", "Save"]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true) + }) + + test("should see the 'Show Local' button on Save Workspace As", async ({ codeServerPage }) => { + // Action + await codeServerPage.navigateMenus(["File", "Save Workspace As..."]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true) + }) +}) + +describe("Show Local (disabled)", ["--disable-workspace-trust", "--disable-show-local"], {}, () => { + const testName = "show-local-disabled" + test.beforeAll(async () => { + await clean(testName) + }) + + test("should not see the 'Show Local' button on Open File", async ({ codeServerPage }) => { + // Action + await codeServerPage.navigateMenus(["File", "Open File..."]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false) + }) + + test("should not see the 'Show Local' button on Save as File'", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const fileName = "unique-file-save-as.txt" + const tmpFilePath = path.join(workspaceDir, fileName) + await fs.writeFile(tmpFilePath, "Hello World") + + // Action + await codeServerPage.page.waitForSelector(`text=${fileName}`) + await codeServerPage.openFile(fileName) + await codeServerPage.page.click(".tab") + await codeServerPage.navigateMenus(["File", "Save As..."]) + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false) + }) + + test("should not see the 'Show Local' button on Save File", async ({ codeServerPage }) => { + // Action + await codeServerPage.navigateMenus(["File", "New Text File"]) + await codeServerPage.waitForTab("Untitled-1") + await codeServerPage.navigateMenus(["File", "Save"]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false) + }) + + test("should not see the 'Show Local' button on Save Workspace As", async ({ codeServerPage }) => { + // Action + await codeServerPage.navigateMenus(["File", "Save Workspace As..."]) + await codeServerPage.page.waitForSelector(".quick-input-widget") + expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false) + }) +}) \ No newline at end of file