Compare commits

...

7 Commits

Author SHA1 Message Date
Benjamin Lu
0bff8eaf99 typo 2025-04-16 15:42:09 -04:00
Benjamin Lu
bbf369c25b Revert extraneous eslint/tsconfig changes 2025-04-16 15:41:27 -04:00
Benjamin Lu
2063b39346 Add mobile viewport playwright test 2025-04-16 09:33:26 -04:00
Benjamin Lu
30f2fb0e7c Merge branch 'main' of https://github.com/Comfy-Org/ComfyUI_frontend into mobile-dvh 2025-04-16 09:09:40 -04:00
Chenlei Hu
06caa21a4d [API Nodes] Setup Google/Github login (#3471) 2025-04-15 20:56:18 -04:00
Benjamin Lu
bdcb8b1137 svh to dvh 2025-04-13 15:04:00 -04:00
Benjamin Lu
28ab12e666 Selective 100vh to 100svh replacement 2025-04-13 14:32:07 -04:00
8 changed files with 167 additions and 24 deletions

View File

@@ -0,0 +1,35 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
test.describe('Mobile viewport', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
})
test('@mobile App UI is not hidden under mobile browser UI', async ({
comfyPage
}) => {
const viewportSize = await comfyPage.page.viewportSize()
// Top menu is visible
const topMenu = comfyPage.page.locator('.comfyui-menu')
await expect(topMenu).toBeVisible()
// Top menu is not cut off from the top
const topMenuBox = await topMenu.boundingBox()
expect(topMenuBox?.y).toBeGreaterThanOrEqual(0)
// Graph is visible
const graphView = comfyPage.page.locator('.lgraphcanvas')
await expect(graphView).toBeVisible()
// Graph is not cut off from the bottom
const graphViewBox = await graphView.boundingBox()
expect(graphViewBox).not.toBeNull()
expect(viewportSize).not.toBeNull()
expect(graphViewBox!.y + graphViewBox!.height).toBeLessThanOrEqual(
viewportSize!.height
)
})
})

View File

@@ -2,7 +2,7 @@
<router-view />
<ProgressSpinner
v-if="isLoading"
class="absolute inset-0 flex justify-center items-center h-screen"
class="absolute inset-0 flex justify-center items-center h-dvh"
/>
<GlobalDialog />
<BlockUI full-screen :blocked="isLoading" />

View File

@@ -42,7 +42,7 @@
body {
width: 100vw;
height: 100vh;
height: 100dvh;
margin: 0;
overflow: hidden;
background: var(--bg-color) var(--bg-img);

View File

@@ -79,6 +79,7 @@ import Divider from 'primevue/divider'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { SignInData, SignUpData } from '@/schemas/signInSchema'
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
@@ -92,33 +93,32 @@ const { onSuccess } = defineProps<{
}>()
const firebaseAuthStore = useFirebaseAuthStore()
const { wrapWithErrorHandlingAsync } = useErrorHandling()
const isSignIn = ref(true)
const toggleState = () => {
isSignIn.value = !isSignIn.value
}
const signInWithGoogle = () => {
// Implement Google login
console.log(isSignIn.value)
console.log('Google login clicked')
const signInWithGoogle = wrapWithErrorHandlingAsync(async () => {
await firebaseAuthStore.loginWithGoogle()
onSuccess()
}
})
const signInWithGithub = () => {
// Implement Github login
console.log(isSignIn.value)
console.log('Github login clicked')
const signInWithGithub = wrapWithErrorHandlingAsync(async () => {
await firebaseAuthStore.loginWithGithub()
onSuccess()
}
})
const signInWithEmail = async (values: SignInData | SignUpData) => {
const { email, password } = values
if (isSignIn.value) {
await firebaseAuthStore.login(email, password)
} else {
await firebaseAuthStore.register(email, password)
const signInWithEmail = wrapWithErrorHandlingAsync(
async (values: SignInData | SignUpData) => {
const { email, password } = values
if (isSignIn.value) {
await firebaseAuthStore.login(email, password)
} else {
await firebaseAuthStore.register(email, password)
}
onSuccess()
}
onSuccess()
}
)
</script>

View File

@@ -1,10 +1,13 @@
import {
type Auth,
GithubAuthProvider,
GoogleAuthProvider,
type User,
type UserCredential,
createUserWithEmailAndPassword,
onAuthStateChanged,
signInWithEmailAndPassword,
signInWithPopup,
signOut
} from 'firebase/auth'
import { defineStore } from 'pinia'
@@ -18,6 +21,10 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
const currentUser = ref<User | null>(null)
const isInitialized = ref(false)
// Providers
const googleProvider = new GoogleAuthProvider()
const githubProvider = new GithubAuthProvider()
// Getters
const isAuthenticated = computed(() => !!currentUser.value)
const userEmail = computed(() => currentUser.value?.email)
@@ -68,6 +75,16 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
createUserWithEmailAndPassword(authInstance, email, password)
)
const loginWithGoogle = async (): Promise<UserCredential> =>
executeAuthAction((authInstance) =>
signInWithPopup(authInstance, googleProvider)
)
const loginWithGithub = async (): Promise<UserCredential> =>
executeAuthAction((authInstance) =>
signInWithPopup(authInstance, githubProvider)
)
const logout = async (): Promise<void> =>
executeAuthAction((authInstance) => signOut(authInstance))
@@ -94,6 +111,8 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
login,
register,
logout,
getIdToken
getIdToken,
loginWithGoogle,
loginWithGithub
}
})

View File

@@ -1,5 +1,5 @@
<template>
<div class="comfyui-body grid h-screen w-screen overflow-hidden">
<div class="comfyui-body grid h-dvh w-screen overflow-hidden">
<div id="comfyui-body-top" class="comfyui-body-top">
<TopMenubar v-if="useNewMenu === 'Top'" />
</div>

View File

@@ -1,5 +1,5 @@
<template>
<main class="w-full min-h-screen overflow-hidden relative">
<main class="w-full min-h-dvh overflow-hidden relative">
<router-view />
</main>
</template>

View File

@@ -13,7 +13,10 @@ vi.mock('firebase/auth', () => ({
signInWithEmailAndPassword: vi.fn(),
createUserWithEmailAndPassword: vi.fn(),
signOut: vi.fn(),
onAuthStateChanged: vi.fn()
onAuthStateChanged: vi.fn(),
signInWithPopup: vi.fn(),
GoogleAuthProvider: vi.fn(),
GithubAuthProvider: vi.fn()
}))
describe('useFirebaseAuthStore', () => {
@@ -272,4 +275,90 @@ describe('useFirebaseAuthStore', () => {
expect(tokenAfterLogout).toBeNull()
})
})
describe('social authentication', () => {
describe('loginWithGoogle', () => {
it('should sign in with Google', async () => {
const mockUserCredential = { user: mockUser }
vi.mocked(firebaseAuth.signInWithPopup).mockResolvedValue(
mockUserCredential as any
)
const result = await store.loginWithGoogle()
expect(firebaseAuth.signInWithPopup).toHaveBeenCalledWith(
mockAuth,
expect.any(firebaseAuth.GoogleAuthProvider)
)
expect(result).toEqual(mockUserCredential)
expect(store.loading).toBe(false)
expect(store.error).toBe(null)
})
it('should handle Google sign in errors', async () => {
const mockError = new Error('Google authentication failed')
vi.mocked(firebaseAuth.signInWithPopup).mockRejectedValue(mockError)
await expect(store.loginWithGoogle()).rejects.toThrow(
'Google authentication failed'
)
expect(firebaseAuth.signInWithPopup).toHaveBeenCalledWith(
mockAuth,
expect.any(firebaseAuth.GoogleAuthProvider)
)
expect(store.loading).toBe(false)
expect(store.error).toBe('Google authentication failed')
})
})
describe('loginWithGithub', () => {
it('should sign in with Github', async () => {
const mockUserCredential = { user: mockUser }
vi.mocked(firebaseAuth.signInWithPopup).mockResolvedValue(
mockUserCredential as any
)
const result = await store.loginWithGithub()
expect(firebaseAuth.signInWithPopup).toHaveBeenCalledWith(
mockAuth,
expect.any(firebaseAuth.GithubAuthProvider)
)
expect(result).toEqual(mockUserCredential)
expect(store.loading).toBe(false)
expect(store.error).toBe(null)
})
it('should handle Github sign in errors', async () => {
const mockError = new Error('Github authentication failed')
vi.mocked(firebaseAuth.signInWithPopup).mockRejectedValue(mockError)
await expect(store.loginWithGithub()).rejects.toThrow(
'Github authentication failed'
)
expect(firebaseAuth.signInWithPopup).toHaveBeenCalledWith(
mockAuth,
expect.any(firebaseAuth.GithubAuthProvider)
)
expect(store.loading).toBe(false)
expect(store.error).toBe('Github authentication failed')
})
})
it('should handle concurrent social login attempts correctly', async () => {
const mockUserCredential = { user: mockUser }
vi.mocked(firebaseAuth.signInWithPopup).mockResolvedValue(
mockUserCredential as any
)
const googleLoginPromise = store.loginWithGoogle()
const githubLoginPromise = store.loginWithGithub()
await Promise.all([googleLoginPromise, githubLoginPromise])
expect(store.loading).toBe(false)
})
})
})