mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
fix: add CloudAuthHelper to bypass Firebase auth guard in cloud E2E tests
The cloud build redirects unauthenticated users to /cloud/login, causing comfyPage.setup() to timeout waiting for window.app. This helper seeds Firebase IndexedDB persistence with a mock user and intercepts Firebase REST APIs so the SDK believes a user is signed in. Auto-activates for @cloud tagged tests in the fixture. Addresses review feedback: https://github.com/Comfy-Org/ComfyUI_frontend/pull/10588#discussion_r2998447273 https://github.com/Comfy-Org/ComfyUI_frontend/pull/10588#discussion_r2998447277
This commit is contained in:
@@ -31,6 +31,7 @@ import { QueueHelper } from './helpers/QueueHelper'
|
||||
import { ClipboardHelper } from './helpers/ClipboardHelper'
|
||||
import { CommandHelper } from './helpers/CommandHelper'
|
||||
import { DragDropHelper } from './helpers/DragDropHelper'
|
||||
import { CloudAuthHelper } from './helpers/CloudAuthHelper'
|
||||
import { FeatureFlagHelper } from './helpers/FeatureFlagHelper'
|
||||
import { KeyboardHelper } from './helpers/KeyboardHelper'
|
||||
import { NodeOperationsHelper } from './helpers/NodeOperationsHelper'
|
||||
@@ -203,6 +204,7 @@ export class ComfyPage {
|
||||
public readonly perf: PerformanceHelper
|
||||
public readonly assets: AssetsHelper
|
||||
public readonly queue: QueueHelper
|
||||
public readonly cloudAuth: CloudAuthHelper
|
||||
|
||||
/** Worker index to test user ID */
|
||||
public readonly userIds: string[] = []
|
||||
@@ -250,6 +252,7 @@ export class ComfyPage {
|
||||
this.perf = new PerformanceHelper(page)
|
||||
this.assets = new AssetsHelper(page)
|
||||
this.queue = new QueueHelper(page)
|
||||
this.cloudAuth = new CloudAuthHelper(page)
|
||||
}
|
||||
|
||||
get visibleToasts() {
|
||||
@@ -462,6 +465,10 @@ export const comfyPageFixture = base.extend<{
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
if (testInfo.tags.includes('@cloud')) {
|
||||
await comfyPage.cloudAuth.mockAuth()
|
||||
}
|
||||
|
||||
await comfyPage.setup()
|
||||
|
||||
const needsPerf =
|
||||
|
||||
171
browser_tests/fixtures/helpers/CloudAuthHelper.ts
Normal file
171
browser_tests/fixtures/helpers/CloudAuthHelper.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Mocks Firebase authentication for cloud E2E tests.
|
||||
*
|
||||
* The cloud build's router guard waits for Firebase `onAuthStateChanged`
|
||||
* to fire, then checks `getAuthHeader()`. In CI no Firebase project is
|
||||
* configured, so the user is never authenticated and the app redirects
|
||||
* to `/cloud/login`.
|
||||
*
|
||||
* This helper seeds Firebase's IndexedDB persistence layer with a mock
|
||||
* user and intercepts the Firebase REST APIs (securetoken, identitytoolkit)
|
||||
* so the SDK believes a user is signed in. Must be called before navigation.
|
||||
*/
|
||||
export class CloudAuthHelper {
|
||||
constructor(private readonly page: Page) {}
|
||||
|
||||
/**
|
||||
* Set up all auth mocks. Must be called before `comfyPage.setup()`.
|
||||
*/
|
||||
async mockAuth(): Promise<void> {
|
||||
await this.seedFirebaseIndexedDB()
|
||||
await this.mockFirebaseEndpoints()
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a lightweight same-origin page to seed Firebase's
|
||||
* IndexedDB persistence with a mock user. This ensures the data
|
||||
* is written before the app loads and Firebase reads it.
|
||||
*
|
||||
* Firebase auth uses `browserLocalPersistence` which stores data in
|
||||
* IndexedDB database `firebaseLocalStorageDb`, object store
|
||||
* `firebaseLocalStorage`, keyed by `firebase:authUser:<apiKey>:<appName>`.
|
||||
*/
|
||||
private async seedFirebaseIndexedDB(): Promise<void> {
|
||||
// Navigate to a lightweight endpoint to get a same-origin context
|
||||
await this.page.goto('http://localhost:8188/api/users')
|
||||
|
||||
await this.page.evaluate(() => {
|
||||
const MOCK_USER_DATA = {
|
||||
uid: 'test-user-e2e',
|
||||
email: 'e2e@test.comfy.org',
|
||||
displayName: 'E2E Test User',
|
||||
emailVerified: true,
|
||||
isAnonymous: false,
|
||||
providerData: [
|
||||
{
|
||||
providerId: 'google.com',
|
||||
uid: 'test-user-e2e',
|
||||
displayName: 'E2E Test User',
|
||||
email: 'e2e@test.comfy.org',
|
||||
phoneNumber: null,
|
||||
photoURL: null
|
||||
}
|
||||
],
|
||||
stsTokenManager: {
|
||||
refreshToken: 'mock-refresh-token',
|
||||
accessToken: 'mock-firebase-id-token',
|
||||
expirationTime: Date.now() + 60 * 60 * 1000
|
||||
},
|
||||
apiKey: 'AIzaSyDa_YMeyzV0SkVe92vBZ1tVikWBmOU5KVE',
|
||||
appName: '[DEFAULT]'
|
||||
}
|
||||
|
||||
const DB_NAME = 'firebaseLocalStorageDb'
|
||||
const STORE_NAME = 'firebaseLocalStorage'
|
||||
const KEY = `firebase:authUser:${MOCK_USER_DATA.apiKey}:${MOCK_USER_DATA.appName}`
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME)
|
||||
request.onerror = () => reject(request.error)
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME)
|
||||
}
|
||||
}
|
||||
request.onsuccess = () => {
|
||||
const db = request.result
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.close()
|
||||
const upgradeReq = indexedDB.open(DB_NAME, db.version + 1)
|
||||
upgradeReq.onerror = () => reject(upgradeReq.error)
|
||||
upgradeReq.onupgradeneeded = () => {
|
||||
const upgradedDb = upgradeReq.result
|
||||
if (!upgradedDb.objectStoreNames.contains(STORE_NAME)) {
|
||||
upgradedDb.createObjectStore(STORE_NAME)
|
||||
}
|
||||
}
|
||||
upgradeReq.onsuccess = () => {
|
||||
const upgradedDb = upgradeReq.result
|
||||
const tx = upgradedDb.transaction(STORE_NAME, 'readwrite')
|
||||
tx.objectStore(STORE_NAME).put(
|
||||
{ fpiVersion: '1', value: MOCK_USER_DATA },
|
||||
KEY
|
||||
)
|
||||
tx.oncomplete = () => {
|
||||
upgradedDb.close()
|
||||
resolve()
|
||||
}
|
||||
tx.onerror = () => reject(tx.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
const tx = db.transaction(STORE_NAME, 'readwrite')
|
||||
tx.objectStore(STORE_NAME).put(
|
||||
{ fpiVersion: '1', value: MOCK_USER_DATA },
|
||||
KEY
|
||||
)
|
||||
tx.oncomplete = () => {
|
||||
db.close()
|
||||
resolve()
|
||||
}
|
||||
tx.onerror = () => reject(tx.error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept Firebase Auth REST API endpoints so the SDK can
|
||||
* "refresh" the mock user's token without real credentials.
|
||||
*/
|
||||
private async mockFirebaseEndpoints(): Promise<void> {
|
||||
await this.page.route('**/securetoken.googleapis.com/**', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
access_token: 'mock-access-token',
|
||||
expires_in: '3600',
|
||||
token_type: 'Bearer',
|
||||
refresh_token: 'mock-refresh-token',
|
||||
id_token: 'mock-firebase-id-token',
|
||||
user_id: 'test-user-e2e',
|
||||
project_id: 'dreamboothy-dev'
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
await this.page.route('**/identitytoolkit.googleapis.com/**', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
kind: 'identitytoolkit#GetAccountInfoResponse',
|
||||
users: [
|
||||
{
|
||||
localId: 'test-user-e2e',
|
||||
email: 'e2e@test.comfy.org',
|
||||
displayName: 'E2E Test User',
|
||||
emailVerified: true,
|
||||
validSince: '0',
|
||||
lastLoginAt: String(Date.now()),
|
||||
createdAt: String(Date.now()),
|
||||
lastRefreshAt: new Date().toISOString()
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
await this.page.route('**/__/auth/**', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: '<html><body></body></html>'
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user