1+ import { randomBytes } from 'node:crypto' ;
12import { env } from 'node:process' ;
23import { expect } from '@playwright/test' ;
34import type { APIRequestContext , Locator , Page } from '@playwright/test' ;
45
5- export function apiBaseUrl ( ) {
6+ export function baseUrl ( ) {
67 return env . GITEA_TEST_E2E_URL ?. replace ( / \/ $ / g, '' ) ;
78}
89
10+ function apiAuthHeader ( username : string , password : string ) {
11+ return { Authorization : `Basic ${ globalThis . btoa ( `${ username } :${ password } ` ) } ` } ;
12+ }
13+
914export function apiHeaders ( ) {
10- return { Authorization : `Basic ${ globalThis . btoa ( ` ${ env . GITEA_TEST_E2E_USER } : ${ env . GITEA_TEST_E2E_PASSWORD } ` ) } ` } ;
15+ return apiAuthHeader ( env . GITEA_TEST_E2E_USER , env . GITEA_TEST_E2E_PASSWORD ) ;
1116}
1217
1318async function apiRetry ( fn : ( ) => Promise < { ok : ( ) => boolean ; status : ( ) => number ; text : ( ) => Promise < string > } > , label : string ) {
@@ -24,30 +29,73 @@ async function apiRetry(fn: () => Promise<{ok: () => boolean; status: () => numb
2429 }
2530}
2631
27- export async function apiCreateRepo ( requestContext : APIRequestContext , { name, autoInit = true } : { name : string ; autoInit ?: boolean } ) {
28- await apiRetry ( ( ) => requestContext . post ( `${ apiBaseUrl ( ) } /api/v1/user/repos` , {
29- headers : apiHeaders ( ) ,
32+ export async function apiCreateRepo ( requestContext : APIRequestContext , { name, autoInit = true , headers } : { name : string ; autoInit ?: boolean ; headers ?: Record < string , string > } ) {
33+ await apiRetry ( ( ) => requestContext . post ( `${ baseUrl ( ) } /api/v1/user/repos` , {
34+ headers : headers || apiHeaders ( ) ,
3035 data : { name, auto_init : autoInit } ,
3136 } ) , 'apiCreateRepo' ) ;
3237}
3338
39+ export async function apiCreateIssue ( requestContext : APIRequestContext , owner : string , repo : string , { title, headers} : { title : string ; headers ?: Record < string , string > } ) {
40+ await apiRetry ( ( ) => requestContext . post ( `${ baseUrl ( ) } /api/v1/repos/${ owner } /${ repo } /issues` , {
41+ headers : headers || apiHeaders ( ) ,
42+ data : { title} ,
43+ } ) , 'apiCreateIssue' ) ;
44+ }
45+
46+ export async function apiStartStopwatch ( requestContext : APIRequestContext , owner : string , repo : string , issueIndex : number , { headers} : { headers ?: Record < string , string > } = { } ) {
47+ await apiRetry ( ( ) => requestContext . post ( `${ baseUrl ( ) } /api/v1/repos/${ owner } /${ repo } /issues/${ issueIndex } /stopwatch/start` , {
48+ headers : headers || apiHeaders ( ) ,
49+ } ) , 'apiStartStopwatch' ) ;
50+ }
51+
3452export async function apiDeleteRepo ( requestContext : APIRequestContext , owner : string , name : string ) {
35- await apiRetry ( ( ) => requestContext . delete ( `${ apiBaseUrl ( ) } /api/v1/repos/${ owner } /${ name } ` , {
53+ await apiRetry ( ( ) => requestContext . delete ( `${ baseUrl ( ) } /api/v1/repos/${ owner } /${ name } ` , {
3654 headers : apiHeaders ( ) ,
3755 } ) , 'apiDeleteRepo' ) ;
3856}
3957
4058export async function apiDeleteOrg ( requestContext : APIRequestContext , name : string ) {
41- await apiRetry ( ( ) => requestContext . delete ( `${ apiBaseUrl ( ) } /api/v1/orgs/${ name } ` , {
59+ await apiRetry ( ( ) => requestContext . delete ( `${ baseUrl ( ) } /api/v1/orgs/${ name } ` , {
4260 headers : apiHeaders ( ) ,
4361 } ) , 'apiDeleteOrg' ) ;
4462}
4563
64+ /** Generate a random password that satisfies the complexity requirements. */
65+ function generatePassword ( ) {
66+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ;
67+ return `${ Array . from ( randomBytes ( 12 ) , ( b ) => chars [ b % chars . length ] ) . join ( '' ) } !aA1` ;
68+ }
69+
70+ /** Random password shared by all test users — used for both API user creation and browser login. */
71+ const testUserPassword = generatePassword ( ) ;
72+
73+ export function apiUserHeaders ( username : string ) {
74+ return apiAuthHeader ( username , testUserPassword ) ;
75+ }
76+
77+ export async function apiCreateUser ( requestContext : APIRequestContext , username : string ) {
78+ await apiRetry ( ( ) => requestContext . post ( `${ baseUrl ( ) } /api/v1/admin/users` , {
79+ headers : apiHeaders ( ) ,
80+ data : { username, password : testUserPassword , email : `${ username } @${ env . GITEA_TEST_E2E_DOMAIN } ` , must_change_password : false } ,
81+ } ) , 'apiCreateUser' ) ;
82+ }
83+
84+ export async function apiDeleteUser ( requestContext : APIRequestContext , username : string ) {
85+ await apiRetry ( ( ) => requestContext . delete ( `${ baseUrl ( ) } /api/v1/admin/users/${ username } ?purge=true` , {
86+ headers : apiHeaders ( ) ,
87+ } ) , 'apiDeleteUser' ) ;
88+ }
89+
4690export async function clickDropdownItem ( page : Page , trigger : Locator , itemText : string ) {
4791 await trigger . click ( ) ;
4892 await page . getByText ( itemText ) . click ( ) ;
4993}
5094
95+ export async function loginUser ( page : Page , username : string ) {
96+ return login ( page , username , testUserPassword ) ;
97+ }
98+
5199export async function login ( page : Page , username = env . GITEA_TEST_E2E_USER , password = env . GITEA_TEST_E2E_PASSWORD ) {
52100 await page . goto ( '/user/login' ) ;
53101 await page . getByLabel ( 'Username or Email Address' ) . fill ( username ) ;
0 commit comments