@@ -3,7 +3,8 @@ import { stubApis, ENV_DATA, COST_DATA } from '../fixtures/index.js';
33
44/**
55 * Auth-gate specs use the base Playwright `test` (no pre-seeded token) so
6- * they can verify the 401 → modal and token-save → reload flows in isolation.
6+ * they can verify the 401 → modal and token-save → inline-retry flows in
7+ * isolation.
78 */
89
910test . describe ( 'auth gate' , ( ) => {
@@ -26,7 +27,7 @@ test.describe('auth gate', () => {
2627 await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . not . toBeVisible ( ) ;
2728 } ) ;
2829
29- test ( 'should save token and show dashboard after reload ' , async ( { page } ) => {
30+ test ( 'should save token and dismiss modal without reloading ' , async ( { page } ) => {
3031 // Playwright matches routes in REVERSE registration order, so register the
3132 // catch-all 404 FIRST and the specific 401/200 handlers after — otherwise
3233 // the catch-all takes precedence and the modal never triggers.
@@ -62,11 +63,43 @@ test.describe('auth gate', () => {
6263 await page . goto ( '/' ) ;
6364 await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . toBeVisible ( ) ;
6465
65- await page . getByPlaceholder ( 'API token' ) . fill ( 'my-test-token' ) ;
66- await page . getByRole ( 'button' , { name : 'Save & reload' } ) . click ( ) ;
66+ // Inline validation requires ≥16 chars, no whitespace.
67+ await page . getByPlaceholder ( 'Paste API token' ) . fill ( 'my-test-token-12345' ) ;
68+ await page . getByRole ( 'button' , { name : 'Save' , exact : true } ) . click ( ) ;
6769
68- // After reload the stored token is sent; the modal must be gone .
70+ // Modal dismisses inline (no reload) and the dashboard surfaces .
6971 await expect ( page . getByRole ( 'heading' , { name : 'Game Server Manager' } ) ) . toBeVisible ( ) ;
7072 await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . not . toBeVisible ( ) ;
7173 } ) ;
74+
75+ test ( 'should show inline validation error for a too-short token' , async ( { page } ) => {
76+ await page . route ( '**/api/**' , ( route ) =>
77+ route . fulfill ( { status : 401 , body : 'Unauthorized' } )
78+ ) ;
79+ await page . goto ( '/' ) ;
80+ await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . toBeVisible ( ) ;
81+
82+ await page . getByPlaceholder ( 'Paste API token' ) . fill ( 'short' ) ;
83+ await page . getByRole ( 'button' , { name : 'Save' , exact : true } ) . click ( ) ;
84+
85+ await expect ( page . getByText ( / a t l e a s t 1 6 c h a r a c t e r s / i) ) . toBeVisible ( ) ;
86+ // Modal stays open — validation never reached the network.
87+ await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . toBeVisible ( ) ;
88+ } ) ;
89+
90+ test ( 'should show inline server error when retry returns 401 again' , async ( { page } ) => {
91+ await page . route ( '**/api/**' , ( route ) =>
92+ route . fulfill ( { status : 401 , body : 'Unauthorized' } )
93+ ) ;
94+ await page . goto ( '/' ) ;
95+ await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . toBeVisible ( ) ;
96+
97+ await page . getByPlaceholder ( 'Paste API token' ) . fill ( 'still-the-wrong-token-1234' ) ;
98+ await page . getByRole ( 'button' , { name : 'Save' , exact : true } ) . click ( ) ;
99+
100+ await expect (
101+ page . getByText ( / I n v a l i d t o k e n — c h e c k ` a p p \/ s e r v e r _ c o n f i g \. j s o n ` o r ` A P I _ T O K E N ` \. / i) ,
102+ ) . toBeVisible ( ) ;
103+ await expect ( page . getByRole ( 'heading' , { name : 'API token required' } ) ) . toBeVisible ( ) ;
104+ } ) ;
72105} ) ;
0 commit comments