@@ -49,8 +49,8 @@ export const before = async ({ page }: { page: Page }) => {
4949 throw new Error ( 'serverUrl is not set' ) ;
5050 }
5151
52- // Open the server
53- await page . goto ( FRONTEND_URL ) ;
52+ // Open the server (use SERVER_URL to support running without frontend dev server)
53+ await page . goto ( SERVER_URL ) ;
5454
5555 // Inject CSS to disable all animations for stable tests
5656 await page . addStyleTag ( {
@@ -89,40 +89,29 @@ export async function setTitle(page: Page, title: string) {
8989 await waiter ;
9090}
9191
92- /** Signs in using an AtomicData.dev test user */
92+ /** Signs in by checking for existing agent or creating one via /setup invite */
9393export async function signIn ( page : Page ) {
94- // Retry login button click with better stability
95- let retries = 3 ;
96- while ( retries > 0 ) {
97- try {
98- await page . click ( 'text=Login' , { timeout : 5000 } ) ;
99- break ;
100- } catch ( error ) {
101- retries -- ;
102- if ( retries === 0 ) throw error ;
103- await page . waitForTimeout ( 100 * ( 4 - retries ) ) ;
104- }
94+ await page . goto ( SERVER_URL ) ;
95+
96+ const hasAgent = await page . evaluate ( ( ) => {
97+ const agent = localStorage . getItem ( 'agent' ) ;
98+ return agent !== null && agent !== '' ;
99+ } ) ;
100+
101+ if ( hasAgent ) {
102+ await page . waitForTimeout ( 500 ) ;
103+ return ;
105104 }
106105
107- // Wait for authentication form to be visible
108- await expect ( page . locator ( 'text=edit data and sign Commits' ) ) . toBeVisible ( { timeout : 10000 } ) ;
106+ await page . goto ( `${ SERVER_URL } /setup` ) ;
109107
110- // If there are any issues with this agent, try creating a new one https://atomicdata.dev/invites/1
111- const test_agent =
112- 'eyJzdWJqZWN0IjoiaHR0cHM6Ly9hdG9taWNkYXRhLmRldi9hZ2VudHMvaElNWHFoR3VLSDRkM0QrV1BjYzAwUHVFbldFMEtlY21GWStWbWNVR2tEWT0iLCJwcml2YXRlS2V5IjoiZkx0SDAvY29VY1BleFluNC85NGxFemFKbUJmZTYxQ3lEekUwODJyMmdRQT0ifQ==' ;
108+ await expect ( page . getByRole ( 'button' , { name : 'Accept' } ) ) . toBeVisible ( { timeout : 10000 } ) ;
113109
114- // Wait for password field to be interactive
115- await page . waitForSelector ( '#current-password' , { state : 'visible' } ) ;
116- await page . click ( '#current-password' ) ;
117- await page . fill ( '#current-password' , test_agent ) ;
110+ await page . getByRole ( 'button' , { name : 'Accept' } ) . click ( ) ;
118111
119- // Wait for successful authentication
120- await expect ( page . locator ( 'text=Edit profile' ) ) . toBeVisible ( { timeout : 10000 } ) ;
112+ await page . waitForURL ( / \/ ( a p p ) ? $ / ) ;
121113
122- // Give WebSocket connection time to stabilize
123114 await page . waitForTimeout ( 500 ) ;
124-
125- await page . goBack ( ) ;
126115}
127116
128117/**
@@ -147,17 +136,16 @@ export async function newDrive(page: Page) {
147136 currentDialog ( page ) . locator ( 'footer button' , { hasText : 'Create' } ) ,
148137 ) . toBeEnabled ( ) ;
149138
150- // Click the create button and wait for dialog to close
139+ const navigationPromise = page . waitForNavigation ( { timeout : 30000 } ) ;
151140 await currentDialog ( page )
152141 . locator ( 'footer button' , { hasText : 'Create' } )
153142 . click ( ) ;
154143
155- // Wait for the dialog to disappear (indicates the action completed)
156- await currentDialog ( page ) . waitFor ( { state : 'hidden' , timeout : 30000 } ) ;
144+ await navigationPromise ;
157145
158146 // Wait for the sidebar to update with the new drive title
159- await expect ( currentDriveTitle ( page ) ) . not . toContainText ( startDriveName ) ;
160- await expect ( currentDriveTitle ( page ) ) . toContainText ( driveTitle ) ;
147+ await expect ( currentDriveTitle ( page ) ) . not . toHaveText ( startDriveName ) ;
148+ await expect ( currentDriveTitle ( page ) ) . toHaveText ( driveTitle ) ;
161149 const driveURL = await getCurrentSubject ( page ) ;
162150 expect ( driveURL ) . toContain ( SERVER_URL ) ;
163151
@@ -238,7 +226,8 @@ export async function waitForCommitOnCurrentResource(
238226}
239227
240228export async function openAgentPage ( page : Page ) {
241- page . goto ( `${ FRONTEND_URL } /app/agent` ) ;
229+ const currentOrigin = new URL ( page . url ( ) ) . origin ;
230+ page . goto ( `${ currentOrigin } /app/agent` ) ;
242231}
243232
244233/** Set atomicdata.dev as current server */
@@ -257,28 +246,9 @@ export async function editProfileAndCommit(page: Page) {
257246 ) . toBeVisible ( ) ;
258247 await expect ( page . getByRole ( 'main' ) . getByText ( 'loading' ) ) . not . toBeVisible ( ) ;
259248
260- // Wait for the page to be fully interactive before clicking
261- await page . waitForLoadState ( 'networkidle' , { timeout : 10000 } ) ;
262-
263- const navigationPromise = page . waitForNavigation ( { timeout : 10000 } ) ;
264-
265- // Retry click with exponential backoff if it fails
266- let retries = 3 ;
267-
268- while ( retries > 0 ) {
269- try {
270- await page . getByRole ( 'button' , { name : 'Edit profile' } ) . click ( ) ;
271- break ;
272- } catch ( error ) {
273- retries -- ;
274- if ( retries === 0 ) throw error ;
275- await page . waitForTimeout ( 100 * ( 4 - retries ) ) ; // 100ms, 200ms, 300ms
276- }
277- }
278-
249+ const navigationPromise = page . waitForNavigation ( { timeout : 5000 } ) ;
250+ await page . getByRole ( 'button' , { name : 'Edit profile' } ) . click ( ) ;
279251 await navigationPromise ;
280-
281- // Find and click the advanced button
282252 const advancedButton = page . getByRole ( 'button' , { name : 'advanced' } ) ;
283253 await advancedButton . scrollIntoViewIfNeeded ( ) ;
284254 await advancedButton . click ( ) ;
@@ -287,7 +257,7 @@ export async function editProfileAndCommit(page: Page) {
287257 await page . getByLabel ( 'Name' ) . fill ( username ) ;
288258 await page . getByRole ( 'button' , { name : 'Save' } ) . click ( ) ;
289259 await expect ( page . locator ( 'text=Resource saved' ) ) . toBeVisible ( ) ;
290- await page . waitForURL ( / \/ a p p \/ s h o w / ) ;
260+ await page . waitForTimeout ( 1000 ) ;
291261 await page . reload ( ) ;
292262 await expect ( page . locator ( `text=${ username } ` ) . first ( ) ) . toBeVisible ( ) ;
293263}
@@ -365,9 +335,13 @@ export async function fillSearchBox(
365335 . first ( ) ;
366336
367337 if ( await container . isVisible ( ) . catch ( ( ) => false ) ) {
368- await container . click ( ) ;
369-
370- return ;
338+ // Retry click if element detaches during click
339+ try {
340+ await container . click ( { timeout : 5000 } ) ;
341+ return ;
342+ } catch ( e ) {
343+ // Element detached, try next strategy
344+ }
371345 }
372346
373347 const optionByRole = ( scope as any )
@@ -402,7 +376,7 @@ export async function fillSearchBox(
402376 * Class can be an Class URL or a shortname available in the new page. */
403377export async function newResource ( klass : string , page : Page ) {
404378 await page . getByTestId ( sideBarNewResourceTestId ) . click ( ) ;
405- await expect ( page ) . toHaveURL ( ` ${ FRONTEND_URL } / app/new` ) ;
379+ await expect ( page ) . toHaveURL ( / \/ a p p \ /n e w $ / ) ;
406380
407381 if ( klass . startsWith ( 'https://' ) ) {
408382 await fillSearchBox ( page , 'Search for a class or enter a URL' , klass ) ;
@@ -418,7 +392,7 @@ export async function newResource(klass: string, page: Page) {
418392export async function openNewSubjectWindow ( browser : Browser , url : string ) {
419393 const context2 = await browser . newContext ( ) ;
420394 const page = await context2 . newPage ( ) ;
421- await page . goto ( FRONTEND_URL ) ;
395+ await page . goto ( SERVER_URL ) ;
422396
423397 // Only when we run on `localhost` we don't need to change drive during tests
424398 if ( SERVER_URL !== FRONTEND_URL ) {
0 commit comments