11import os from 'node:os' ;
22import path from 'node:path' ;
3+ import fs from 'node:fs/promises' ;
34import type { Page } from '@playwright/test' ;
45import { expect , test } from '@playwright/test' ;
56
67const HOME_DIR = os . homedir ( ) ;
78const HOME_LABEL = path . basename ( HOME_DIR ) || HOME_DIR ;
9+ const TAB_STABILITY_DIRS = [
10+ path . join ( HOME_DIR , 'coder-studio-e2e-tab-a' ) ,
11+ path . join ( HOME_DIR , 'coder-studio-e2e-tab-b' ) ,
12+ ] ;
13+ const TAB_STABILITY_LABELS = TAB_STABILITY_DIRS . map ( ( dir ) => path . basename ( dir ) ) ;
814
915const openLaunchOverlay = async ( page : Page ) => {
1016 await page . goto ( '/' ) ;
@@ -34,6 +40,29 @@ const launchLocalWorkspace = async (page: Page) => {
3440 await expect ( page . getByTestId ( 'workspace-topbar' ) ) . toBeVisible ( ) ;
3541} ;
3642
43+ const invokeRpc = async < T > ( page : Page , command : string , payload : Record < string , unknown > = { } ) => {
44+ const response = await page . request . post ( `/api/rpc/${ command } ` , { data : payload } ) ;
45+ expect ( response . ok ( ) ) . toBeTruthy ( ) ;
46+ const body = await response . json ( ) ;
47+ expect ( body . ok ) . not . toBe ( false ) ;
48+ return body . data as T ;
49+ } ;
50+
51+ const launchWorkspaceByPath = async ( page : Page , workspacePath : string ) => {
52+ await invokeRpc ( page , 'launch_workspace' , {
53+ source : {
54+ kind : 'local' ,
55+ pathOrUrl : workspacePath ,
56+ target : { type : 'native' } ,
57+ } ,
58+ } ) ;
59+ } ;
60+
61+ const readWorkspaceTabLabels = async ( page : Page ) =>
62+ page . locator ( '.workspace-top-tab .session-top-label' ) . evaluateAll ( ( nodes ) =>
63+ nodes . map ( ( node ) => node . textContent ?. trim ( ) ?? '' ) . filter ( Boolean )
64+ ) ;
65+
3766test . beforeEach ( async ( { page } ) => {
3867 await page . addInitScript ( ( ) => {
3968 if ( ! window . sessionStorage . getItem ( 'coder-studio.test-init' ) ) {
@@ -45,6 +74,14 @@ test.beforeEach(async ({ page }) => {
4574 } ) ;
4675} ) ;
4776
77+ test . beforeAll ( async ( ) => {
78+ await Promise . all ( TAB_STABILITY_DIRS . map ( ( dir ) => fs . mkdir ( dir , { recursive : true } ) ) ) ;
79+ } ) ;
80+
81+ test . afterAll ( async ( ) => {
82+ await Promise . all ( TAB_STABILITY_DIRS . map ( ( dir ) => fs . rm ( dir , { recursive : true , force : true } ) ) ) ;
83+ } ) ;
84+
4885test ( 'local workspace flow opens the workspace shell' , async ( { page } ) => {
4986 await launchLocalWorkspace ( page ) ;
5087 await expect ( page . getByTestId ( 'workspace-topbar' ) ) . toContainText ( HOME_LABEL ) ;
@@ -95,3 +132,24 @@ test('restores the last workspace after reload', async ({ page }) => {
95132 await expect ( page . getByTestId ( 'overlay' ) ) . toHaveCount ( 0 ) ;
96133 await expect ( page . getByTestId ( 'workspace-topbar' ) ) . toContainText ( HOME_LABEL ) ;
97134} ) ;
135+
136+ test ( 'workspace tabs keep a stable order when switching between workspaces' , async ( { page } ) => {
137+ await launchWorkspaceByPath ( page , TAB_STABILITY_DIRS [ 0 ] ) ;
138+ await launchWorkspaceByPath ( page , TAB_STABILITY_DIRS [ 1 ] ) ;
139+ await page . goto ( '/' ) ;
140+ await expect ( page . getByTestId ( 'workspace-topbar' ) ) . toBeVisible ( ) ;
141+
142+ const initialOrder = await readWorkspaceTabLabels ( page ) ;
143+ const initialFirstIndex = initialOrder . indexOf ( TAB_STABILITY_LABELS [ 0 ] ) ;
144+ const initialSecondIndex = initialOrder . indexOf ( TAB_STABILITY_LABELS [ 1 ] ) ;
145+ expect ( initialFirstIndex ) . toBeGreaterThanOrEqual ( 0 ) ;
146+ expect ( initialSecondIndex ) . toBeGreaterThan ( initialFirstIndex ) ;
147+
148+ await page . locator ( '.workspace-top-tab' ) . filter ( { hasText : TAB_STABILITY_LABELS [ 0 ] } ) . click ( ) ;
149+ await expect ( page . locator ( '.workspace-top-tab.active .session-top-label' ) ) . toHaveText ( TAB_STABILITY_LABELS [ 0 ] ) ;
150+ await expect . poll ( ( ) => readWorkspaceTabLabels ( page ) ) . toEqual ( initialOrder ) ;
151+
152+ await page . locator ( '.workspace-top-tab' ) . filter ( { hasText : TAB_STABILITY_LABELS [ 1 ] } ) . click ( ) ;
153+ await expect ( page . locator ( '.workspace-top-tab.active .session-top-label' ) ) . toHaveText ( TAB_STABILITY_LABELS [ 1 ] ) ;
154+ await expect . poll ( ( ) => readWorkspaceTabLabels ( page ) ) . toEqual ( initialOrder ) ;
155+ } ) ;
0 commit comments