Refine auth page tests to avoid snapshots (#614)
This commit is contained in:
parent
e65e5078ce
commit
fa3ea52e2b
13
dashboard/app/(auth)/layout.tsx
Normal file
13
dashboard/app/(auth)/layout.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
import { AppShellBypass } from '@lib/appShellBypass'
|
||||
|
||||
export default function AuthPagesLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<AppShellBypass>
|
||||
<div className="flex min-h-screen flex-col bg-slate-50">
|
||||
{children}
|
||||
</div>
|
||||
</AppShellBypass>
|
||||
)
|
||||
}
|
||||
@ -5,13 +5,12 @@ import Link from 'next/link'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { Github } from 'lucide-react'
|
||||
|
||||
import { AskAIButton } from '@components/AskAIButton'
|
||||
import { AuthLayout, AuthLayoutSocialButton } from '@components/auth/AuthLayout'
|
||||
import { useLanguage } from '@i18n/LanguageProvider'
|
||||
import { translations } from '@i18n/translations'
|
||||
import { getAccountServiceBaseUrl } from '@lib/serviceConfig'
|
||||
|
||||
import { WeChatIcon } from '../components/icons/WeChatIcon'
|
||||
import { WeChatIcon } from '../../components/icons/WeChatIcon'
|
||||
|
||||
type LoginContentProps = {
|
||||
children?: ReactNode
|
||||
@ -346,7 +345,6 @@ export default function LoginContent({ children }: LoginContentProps) {
|
||||
>
|
||||
{formContent}
|
||||
</AuthLayout>
|
||||
<AskAIButton />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -5,13 +5,12 @@ import { ChevronDown, Github } from 'lucide-react'
|
||||
import { FormEvent, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
|
||||
import { AskAIButton } from '@components/AskAIButton'
|
||||
import { AuthLayout, AuthLayoutSocialButton } from '@components/auth/AuthLayout'
|
||||
import { useLanguage } from '@i18n/LanguageProvider'
|
||||
import { translations } from '@i18n/translations'
|
||||
import { getAccountServiceBaseUrl } from '@lib/serviceConfig'
|
||||
|
||||
import { WeChatIcon } from '../components/icons/WeChatIcon'
|
||||
import { WeChatIcon } from '../../components/icons/WeChatIcon'
|
||||
|
||||
type AlertState = { type: 'error' | 'success'; message: string }
|
||||
|
||||
@ -498,7 +497,6 @@ export default function RegisterContent() {
|
||||
</button>
|
||||
</form>
|
||||
</AuthLayout>
|
||||
<AskAIButton />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
import { ComponentType, createElement, type ReactNode } from 'react'
|
||||
import { ComponentType, createElement, isValidElement, type ReactNode } from 'react'
|
||||
|
||||
import { cmsConfig, type ExtensionName, type ThemeName } from './config'
|
||||
import type { CmsExtension, CmsTheme } from './types'
|
||||
import { appShellExtension } from './extensions/appShell'
|
||||
import { markdownSyncExtension } from './extensions/markdownSync'
|
||||
import defaultTheme from './themes/default'
|
||||
import { AppShellBypass } from '../lib/appShellBypass'
|
||||
|
||||
const themeRegistry: Record<ThemeName, CmsTheme> = {
|
||||
default: defaultTheme,
|
||||
@ -30,11 +31,24 @@ export function collectExtensionProviders(): ComponentType<{ children: ReactNode
|
||||
}
|
||||
|
||||
export function applyExtensionLayouts(children: ReactNode): ReactNode {
|
||||
const { content, skipAppShell } = unwrapAppShellBypass(children)
|
||||
|
||||
return getActiveExtensions().reduceRight((acc, extension) => {
|
||||
if (!extension.Layout) {
|
||||
return acc
|
||||
}
|
||||
if (skipAppShell && extension.name === 'app-shell') {
|
||||
return acc
|
||||
}
|
||||
const LayoutComponent = extension.Layout
|
||||
return createElement(LayoutComponent, null, acc)
|
||||
}, children)
|
||||
}, content)
|
||||
}
|
||||
|
||||
function unwrapAppShellBypass(node: ReactNode): { content: ReactNode; skipAppShell: boolean } {
|
||||
if (isValidElement(node) && node.type === AppShellBypass) {
|
||||
return { content: node.props.children, skipAppShell: true }
|
||||
}
|
||||
|
||||
return { content: node, skipAppShell: false }
|
||||
}
|
||||
|
||||
@ -98,7 +98,10 @@ export function AuthLayout({
|
||||
className="pointer-events-none absolute inset-x-0 -top-1/3 h-1/2 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-sky-100 via-transparent to-transparent"
|
||||
aria-hidden
|
||||
/>
|
||||
<main className="relative flex flex-1 items-center justify-center px-4 py-12 sm:px-6 lg:px-8">
|
||||
<main
|
||||
className="relative flex flex-1 items-center justify-center px-4 py-12 sm:px-6 lg:px-8"
|
||||
data-testid="auth-layout"
|
||||
>
|
||||
<div className="w-full max-w-md">
|
||||
<div className="mb-8 text-center">
|
||||
<Link href="/" className="text-3xl font-semibold tracking-tight text-slate-900">
|
||||
|
||||
5
dashboard/lib/appShellBypass.tsx
Normal file
5
dashboard/lib/appShellBypass.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export function AppShellBypass({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>
|
||||
}
|
||||
63
dashboard/ui/tests/e2e/auth-pages.spec.ts
Normal file
63
dashboard/ui/tests/e2e/auth-pages.spec.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Page, expect, test } from '@playwright/test'
|
||||
|
||||
async function expectAuthLayoutWithinViewport(page: Page) {
|
||||
const viewport = page.viewportSize()
|
||||
if (!viewport) {
|
||||
throw new Error('Viewport size is not available')
|
||||
}
|
||||
|
||||
const authLayout = page.getByTestId('auth-layout')
|
||||
await expect(authLayout).toBeVisible()
|
||||
|
||||
const boundingBox = await authLayout.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect()
|
||||
return {
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left,
|
||||
}
|
||||
})
|
||||
|
||||
const tolerance = 1
|
||||
expect.soft(boundingBox.top).toBeGreaterThanOrEqual(-tolerance)
|
||||
expect.soft(boundingBox.left).toBeGreaterThanOrEqual(-tolerance)
|
||||
expect.soft(boundingBox.right).toBeLessThanOrEqual(viewport.width + tolerance)
|
||||
expect.soft(boundingBox.bottom).toBeLessThanOrEqual(viewport.height + tolerance)
|
||||
|
||||
await expect(page.locator('nav')).toHaveCount(0)
|
||||
}
|
||||
|
||||
test.describe('Auth pages', () => {
|
||||
test('login form visible on desktop viewport', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 720 })
|
||||
await page.goto('/login')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
await expectAuthLayoutWithinViewport(page)
|
||||
})
|
||||
|
||||
test('login form visible on mobile viewport', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 812 })
|
||||
await page.goto('/login')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
await expectAuthLayoutWithinViewport(page)
|
||||
})
|
||||
|
||||
test('register form visible on desktop viewport', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 720 })
|
||||
await page.goto('/register')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
await expectAuthLayoutWithinViewport(page)
|
||||
})
|
||||
|
||||
test('register form visible on mobile viewport', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 812 })
|
||||
await page.goto('/register')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
await expectAuthLayoutWithinViewport(page)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user