11import os from 'node:os' ;
22import path from 'node:path' ;
33import fs from 'node:fs/promises' ;
4- import type { Page } from '@playwright/test' ;
4+ import type { APIRequestContext , Page } from '@playwright/test' ;
55import { expect , test } from '@playwright/test' ;
66
77const HOME_DIR = os . homedir ( ) ;
@@ -11,6 +11,8 @@ const TAB_STABILITY_DIRS = [
1111 path . join ( HOME_DIR , 'coder-studio-e2e-tab-b' ) ,
1212] ;
1313const TAB_STABILITY_LABELS = TAB_STABILITY_DIRS . map ( ( dir ) => path . basename ( dir ) ) ;
14+ const REMOTE_HTTP_HOST = 'coder-studio.test' ;
15+ const REMOTE_HTTP_PASSWORD = 'demo-passphrase' ;
1416
1517const gotoWorkspaceRoot = async ( page : Page ) => {
1618 await Promise . all ( [
@@ -65,6 +67,30 @@ const invokeRpc = async <T>(page: Page, command: string, payload: Record<string,
6567 return body . data as T ;
6668} ;
6769
70+ const patchSystemConfig = async ( request : APIRequestContext , updates : Record < string , unknown > ) => {
71+ const response = await request . patch ( 'http://127.0.0.1:4173/api/system/config' , {
72+ data : { updates } ,
73+ } ) ;
74+ expect ( response . ok ( ) ) . toBeTruthy ( ) ;
75+ const body = await response . json ( ) ;
76+ expect ( body . ok ) . not . toBe ( false ) ;
77+ return body . data as {
78+ config : {
79+ root : { path ?: string | null } ;
80+ } ;
81+ } ;
82+ } ;
83+
84+ const readSystemConfig = async ( request : APIRequestContext ) => {
85+ const response = await request . get ( 'http://127.0.0.1:4173/api/system/config' ) ;
86+ expect ( response . ok ( ) ) . toBeTruthy ( ) ;
87+ const body = await response . json ( ) ;
88+ expect ( body . ok ) . not . toBe ( false ) ;
89+ return body . data as {
90+ root : { path ?: string | null } ;
91+ } ;
92+ } ;
93+
6894const launchWorkspaceByPath = async ( page : Page , workspacePath : string ) => {
6995 await invokeRpc ( page , 'launch_workspace' , {
7096 source : {
@@ -170,3 +196,42 @@ test('workspace tabs keep a stable order when switching between workspaces', asy
170196 await expect ( page . locator ( '.workspace-top-tab.active .session-top-label' ) ) . toHaveText ( TAB_STABILITY_LABELS [ 1 ] ) ;
171197 await expect . poll ( ( ) => readWorkspaceTabLabels ( page ) ) . toEqual ( initialOrder ) ;
172198} ) ;
199+
200+ test ( 'release runtime allows sign-in from a remote HTTP host' , async ( { page, request, baseURL } ) => {
201+ test . skip ( ! baseURL ?. includes ( ':4173' ) , 'Release runtime only' ) ;
202+
203+ const currentConfig = await readSystemConfig ( request ) ;
204+ const originalRootPath = currentConfig . root . path ?? null ;
205+
206+ try {
207+ await patchSystemConfig ( request , {
208+ 'auth.password' : REMOTE_HTTP_PASSWORD ,
209+ 'root.path' : process . cwd ( ) ,
210+ } ) ;
211+
212+ await page . route ( '**/*' , async ( route ) => {
213+ await route . continue ( {
214+ headers : {
215+ ...route . request ( ) . headers ( ) ,
216+ 'x-forwarded-host' : REMOTE_HTTP_HOST ,
217+ 'x-forwarded-proto' : 'http' ,
218+ } ,
219+ } ) ;
220+ } ) ;
221+
222+ await page . goto ( '/?auth=force' ) ;
223+ await expect ( page . getByRole ( 'heading' , { name : 'Unlock Coder Studio' } ) ) . toBeVisible ( ) ;
224+ await expect ( page . getByText ( 'HTTPS is required on this host' ) ) . toHaveCount ( 0 ) ;
225+
226+ await page . getByPlaceholder ( 'Enter passphrase' ) . fill ( REMOTE_HTTP_PASSWORD ) ;
227+ await page . getByRole ( 'button' , { name : 'Enter workspace' } ) . click ( ) ;
228+
229+ await expect ( page . getByTestId ( 'overlay' ) ) . toBeVisible ( ) ;
230+ await expect ( page . getByTestId ( 'folder-select' ) ) . toBeVisible ( ) ;
231+ } finally {
232+ await patchSystemConfig ( request , {
233+ 'auth.password' : null ,
234+ 'root.path' : originalRootPath ,
235+ } ) ;
236+ }
237+ } ) ;
0 commit comments