@@ -30,6 +30,27 @@ async function signInWithOAuth(page: Page): Promise<void> {
3030 await page . waitForURL ( / \/ p r o f i l e / , { timeout : 30_000 } ) ;
3131}
3232
33+ /**
34+ * Drives the login flow from the home page's client `signIn('mock-oidc')`
35+ * button. Because the helper performs a CSRF-protected POST straight to
36+ * `/api/auth/signin/mock-oidc`, the browser lands directly on the Mock OIDC
37+ * username form — there is no Auth.js provider chooser to click through. This
38+ * is the path that regressed to the Configuration error before the fix.
39+ *
40+ * @param page - The Playwright Page instance
41+ * @returns Resolves once the browser has navigated to /profile
42+ */
43+ async function signInWithClientHelper ( page : Page ) : Promise < void > {
44+ // Wait for hydration: the button calls the client `signIn` helper, so its
45+ // click handler is only wired once the page's JavaScript has loaded.
46+ await page . goto ( '/' , { waitUntil : 'networkidle' } ) ;
47+ await page . click ( '[data-testid="signin-oauth"]' ) ;
48+ await page . waitForSelector ( 'input[name="username"]' , { timeout : 30_000 } ) ;
49+ await page . fill ( 'input[name="username"]' , 'testuser' ) ;
50+ await page . locator ( '[type="submit"]' ) . first ( ) . click ( ) ;
51+ await page . waitForURL ( / \/ p r o f i l e / , { timeout : 30_000 } ) ;
52+ }
53+
3354test . beforeAll (
3455 async ( ) => {
3556 container = await new GenericContainer (
@@ -95,39 +116,41 @@ test.afterAll(async () => {
95116
96117test ( 'homepage shows unauthenticated state' , async ( { page } ) => {
97118 await page . goto ( '/' ) ;
119+ await expect ( page . locator ( '[data-testid="signin-oauth"]' ) ) . toBeVisible ( ) ;
120+ await expect ( page . locator ( '[data-testid="signin-default"]' ) ) . toBeVisible ( ) ;
98121 await expect (
99122 page . locator ( '[data-testid="signin-credentials"]' ) ,
100123 ) . toBeVisible ( ) ;
101- await expect ( page . locator ( '[data-testid="signin-oauth"]' ) ) . toBeVisible ( ) ;
102124} ) ;
103125
104- test ( 'OAuth sign-in via signin-oauth button' , async ( { page } ) => {
105- await page . goto ( '/' ) ;
106- await page . click ( '[data-testid="signin-oauth"]' ) ;
107- // Auth.js renders a provider confirmation page for GET /signin/:provider;
108- // click through it to initiate the actual OAuth flow.
109- await page . waitForSelector ( 'text=Mock OIDC' , { timeout : 15_000 } ) ;
110- await page . click ( 'text=Mock OIDC' ) ;
111- await page . waitForSelector ( 'input[name="username"]' , { timeout : 15_000 } ) ;
112- await page . fill ( 'input[name="username"]' , 'testuser' ) ;
113- await page . locator ( '[type="submit"]' ) . first ( ) . click ( ) ;
114- await page . waitForURL ( / \/ p r o f i l e / , { timeout : 30_000 } ) ;
126+ // Side path: the home page's client `signIn('mock-oidc')` button. This
127+ // exercises the SDK client helper end-to-end — the path that previously threw
128+ // a Configuration error because the helper issued a GET instead of a POST.
129+ test ( 'OAuth sign-in via home client-helper button' , async ( { page } ) => {
130+ await signInWithClientHelper ( page ) ;
131+ await expect ( page ) . toHaveURL ( / \/ p r o f i l e / ) ;
115132 await expect ( page . locator ( '[data-testid="signout-button"]' ) ) . toBeVisible ( ) ;
116133} ) ;
117134
135+ // Direct route: the Auth.js-rendered provider chooser at /api/auth/signin.
118136test ( 'full OAuth flow via Auth.js sign-in page' , async ( { page } ) => {
119137 await signInWithOAuth ( page ) ;
120138 await expect ( page ) . toHaveURL ( / \/ p r o f i l e / ) ;
121139} ) ;
122140
123- test ( 'full sign-in and sign-out cycle' , async ( { page } ) => {
124- await signInWithOAuth ( page ) ;
125- await page . goto ( '/' ) ;
141+ // Logout component: the one-click client `signOut()` button on the profile
142+ // page clears the session and returns to the unauthenticated home page.
143+ test ( 'sign-out via logout component' , async ( { page } ) => {
144+ await signInWithClientHelper ( page ) ;
145+ // Wait for hydration so the one-click signOut handler is wired.
146+ await page . waitForLoadState ( 'networkidle' ) ;
126147 await page . click ( '[data-testid="signout-button"]' ) ;
127- await page . locator ( 'button[type="submit"]' ) . click ( ) ;
128- await page . waitForURL ( ( url ) => ! url . pathname . startsWith ( '/api/auth/' ) , {
129- timeout : 10_000 ,
148+ // signOut clears the session, so the protected profile page is no longer
149+ // reachable (the app bounces unauthenticated users away from it).
150+ await page . waitForURL ( ( url ) => ! url . pathname . startsWith ( '/profile' ) , {
151+ timeout : 30_000 ,
130152 } ) ;
153+ await page . goto ( '/' ) ;
131154 await expect (
132155 page . locator ( '[data-testid="signin-credentials"]' ) ,
133156 ) . toBeVisible ( ) ;
0 commit comments