Skip to content

Commit 947dd85

Browse files
authored
Merge pull request coder#2922 from cdr/jsjoeio/add-logout
feat(lib/vscode): add log out to application menu
2 parents 24dc208 + 9eff1f0 commit 947dd85

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export enum Cookie {
2+
Key = 'key',
3+
}

lib/vscode/src/vs/workbench/browser/parts/titlebar/menubarControl.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/com
99
import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
1010
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1111
import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions';
12-
import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom';
12+
import { addDisposableListener, Dimension, EventType, getCookieValue } from 'vs/base/browser/dom';
1313
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1414
import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform';
1515
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
@@ -38,6 +38,8 @@ import { KeyCode } from 'vs/base/common/keyCodes';
3838
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
3939
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
4040
import { ICommandService } from 'vs/platform/commands/common/commands';
41+
import { ILogService } from 'vs/platform/log/common/log';
42+
import { Cookie } from 'vs/server/common/cookie';
4143

4244
export abstract class MenubarControl extends Disposable {
4345

@@ -312,7 +314,8 @@ export class CustomMenubarControl extends MenubarControl {
312314
@IThemeService private readonly themeService: IThemeService,
313315
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
314316
@IHostService protected readonly hostService: IHostService,
315-
@ICommandService commandService: ICommandService
317+
@ICommandService commandService: ICommandService,
318+
@ILogService private readonly logService: ILogService
316319
) {
317320
super(menuService, workspacesService, contextKeyService, keybindingService, configurationService, labelService, updateService, storageService, notificationService, preferencesService, environmentService, accessibilityService, hostService, commandService);
318321

@@ -711,6 +714,28 @@ export class CustomMenubarControl extends MenubarControl {
711714
webNavigationActions.pop();
712715
}
713716

717+
webNavigationActions.push(new Action('logout', localize('logout', "Log out"), undefined, true,
718+
async (event?: MouseEvent) => {
719+
const COOKIE_KEY = Cookie.Key;
720+
const loginCookie = getCookieValue(COOKIE_KEY);
721+
722+
this.logService.info('Logging out of code-server');
723+
724+
if(loginCookie) {
725+
this.logService.info(`Removing cookie under ${COOKIE_KEY}`);
726+
727+
if (document && document.cookie) {
728+
// We delete the cookie by setting the expiration to a date/time in the past
729+
document.cookie = COOKIE_KEY +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
730+
window.location.href = '/login';
731+
} else {
732+
this.logService.warn('Could not delete cookie because document and/or document.cookie is undefined');
733+
}
734+
} else {
735+
this.logService.warn('Could not log out because we could not find cookie');
736+
}
737+
}));
738+
714739
return webNavigationActions;
715740
}
716741

test/e2e/logout.test.ts

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { chromium, Page, Browser, BrowserContext } from "playwright"
2+
import { CODE_SERVER_ADDRESS, PASSWORD, E2E_VIDEO_DIR } from "../utils/constants"
3+
4+
describe("logout", () => {
5+
let browser: Browser
6+
let page: Page
7+
let context: BrowserContext
8+
9+
beforeAll(async () => {
10+
browser = await chromium.launch()
11+
context = await browser.newContext({
12+
recordVideo: { dir: E2E_VIDEO_DIR },
13+
})
14+
})
15+
16+
afterAll(async () => {
17+
await browser.close()
18+
})
19+
20+
beforeEach(async () => {
21+
page = await context.newPage()
22+
})
23+
24+
afterEach(async () => {
25+
await page.close()
26+
// Remove password from local storage
27+
await context.clearCookies()
28+
})
29+
30+
it("should be able login and logout", async () => {
31+
await page.goto(CODE_SERVER_ADDRESS)
32+
// Type in password
33+
await page.fill(".password", PASSWORD)
34+
// Click the submit button and login
35+
await page.click(".submit")
36+
// See the editor
37+
const codeServerEditor = await page.isVisible(".monaco-workbench")
38+
expect(codeServerEditor).toBeTruthy()
39+
40+
// Click the Application menu
41+
await page.click("[aria-label='Application Menu']")
42+
43+
// See the Log out button
44+
const logoutButton = "a.action-menu-item span[aria-label='Log out']"
45+
expect(await page.isVisible(logoutButton))
46+
47+
await page.hover(logoutButton)
48+
49+
await page.click(logoutButton)
50+
// it takes a couple seconds to navigate
51+
await page.waitForTimeout(2000)
52+
const currentUrl = page.url()
53+
expect(currentUrl).toBe(`${CODE_SERVER_ADDRESS}/login`)
54+
})
55+
})

0 commit comments

Comments
 (0)