A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

at trunk 2.6 kB View raw
1import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; 2import { act, createElement, useContext, type ReactNode } from 'react'; 3import { createRoot } from 'react-dom/client'; 4import { AuthContext, AuthProvider, type AuthContextValue } from './AuthProvider'; 5 6( globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean } ).IS_REACT_ACT_ENVIRONMENT = true; 7 8// A signed-out browser OAuth client whose `signIn` we capture. `init()` resolves with no 9// session so the provider settles on 'signed-out' without touching the PDS/profile code. 10const oauth = vi.hoisted( () => ( { 11 signIn: vi.fn( async () => {} ), 12} ) ); 13vi.mock( './oauth', () => ( { 14 createOAuthClient: async () => ( { 15 init: async () => null, 16 signIn: oauth.signIn, 17 revoke: async () => {}, 18 } ), 19} ) ); 20 21/** Render the provider and hand back the live context value (incl. `signIn`). */ 22async function mountProvider() { 23 const captured: { value: AuthContextValue | null } = { value: null }; 24 function Capture( { sink }: { sink: { value: AuthContextValue | null } } ): ReactNode { 25 sink.value = useContext( AuthContext ); 26 return null; 27 } 28 const container = document.createElement( 'div' ); 29 const root = createRoot( container ); 30 await act( async () => { 31 root.render( createElement( AuthProvider, null, createElement( Capture, { sink: captured } ) ) ); 32 } ); 33 return captured; 34} 35 36function setLocation( origin: string, pathname: string, hostname: string ) { 37 vi.stubGlobal( 'location', { origin, pathname, hostname } ); 38} 39 40describe( 'AuthProvider signIn redirect target', () => { 41 beforeEach( () => { 42 oauth.signIn.mockClear(); 43 } ); 44 afterEach( () => { 45 vi.unstubAllGlobals(); 46 } ); 47 48 it( 'hosted: returns to the page sign-in started from (/write/, not /editor/)', async () => { 49 setLocation( 'https://skypress.blog', '/write/', 'skypress.blog' ); 50 const ctx = await mountProvider(); 51 await act( async () => { 52 await ctx.value!.signIn( 'alice.bsky.social' ); 53 } ); 54 expect( oauth.signIn ).toHaveBeenCalledWith( 'alice.bsky.social', { 55 redirect_uri: 'https://skypress.blog/write/', 56 } ); 57 } ); 58 59 it( 'loopback (dev): passes no redirect_uri — the per-page loopback client already round-trips', async () => { 60 setLocation( 'http://127.0.0.1:4321', '/write', '127.0.0.1' ); 61 const ctx = await mountProvider(); 62 await act( async () => { 63 await ctx.value!.signIn( 'alice.bsky.social' ); 64 } ); 65 // `undefined` options is equivalent to omitting them — the loopback metadata's lone 66 // redirect URI is the current page, so atproto comes back here on its own. 67 expect( oauth.signIn ).toHaveBeenCalledWith( 'alice.bsky.social', undefined ); 68 } ); 69} );