11import { expect } from "@playwright/test" ;
2+ import { createServer } from "http" ;
3+ import type { AddressInfo } from "net" ;
24import { testWithUserScripts } from "./fixtures" ;
35import { runTestScript } from "./utils" ;
46
57const TARGET_URL = "https://content-security-policy.com/" ;
8+ const GITHUB_REPO_API_URL = "https://api.github.com/repos/scriptscat/scriptcat" ;
9+ const MOCK_CONNECT_HOST = "127.0.0.1" ;
10+
11+ type GMApiMockServer = {
12+ origin : string ;
13+ close : ( ) => Promise < void > ;
14+ } ;
15+
16+ async function startGMApiMockServer ( ) : Promise < GMApiMockServer > {
17+ const server = createServer ( ( req , res ) => {
18+ res . setHeader ( "Access-Control-Allow-Origin" , "*" ) ;
19+
20+ if ( req . method === "OPTIONS" ) {
21+ res . writeHead ( 204 ) ;
22+ res . end ( ) ;
23+ return ;
24+ }
25+
26+ if ( req . url === "/repos/scriptscat/scriptcat" ) {
27+ res . writeHead ( 200 , { "Content-Type" : "application/json" } ) ;
28+ res . end (
29+ JSON . stringify ( {
30+ name : "scriptcat" ,
31+ full_name : "scriptscat/scriptcat" ,
32+ description : "ScriptCat" ,
33+ } )
34+ ) ;
35+ return ;
36+ }
37+
38+ res . writeHead ( 404 , { "Content-Type" : "text/plain" } ) ;
39+ res . end ( "not found" ) ;
40+ } ) ;
41+
42+ await new Promise < void > ( ( resolve , reject ) => {
43+ const onError = ( error : Error ) => reject ( error ) ;
44+ server . once ( "error" , onError ) ;
45+ server . listen ( 0 , MOCK_CONNECT_HOST , ( ) => {
46+ server . off ( "error" , onError ) ;
47+ resolve ( ) ;
48+ } ) ;
49+ } ) ;
50+
51+ const address = server . address ( ) as AddressInfo ;
52+ return {
53+ origin : `http://${ MOCK_CONNECT_HOST } :${ address . port } ` ,
54+ close : ( ) =>
55+ new Promise < void > ( ( resolve , reject ) => {
56+ server . close ( ( error ) => {
57+ if ( error ) reject ( error ) ;
58+ else resolve ( ) ;
59+ } ) ;
60+ } ) ,
61+ } ;
62+ }
63+
64+ function patchGMApiTestCode ( code : string , mockOrigin : string ) : string {
65+ return code
66+ . replace ( / ^ \/ \/ \s * @ c o n n e c t \s + a p i \. g i t h u b \. c o m $ / gm, `// @connect ${ MOCK_CONNECT_HOST } ` )
67+ . replace (
68+ new RegExp ( GITHUB_REPO_API_URL . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) , "g" ) ,
69+ `${ mockOrigin } /repos/scriptscat/scriptcat`
70+ ) ;
71+ }
672
773testWithUserScripts . describe ( "GM API" , ( ) => {
74+ let gmApiMockServer : GMApiMockServer ;
75+
76+ testWithUserScripts . beforeAll ( async ( ) => {
77+ gmApiMockServer = await startGMApiMockServer ( ) ;
78+ } ) ;
79+
80+ testWithUserScripts . afterAll ( async ( ) => {
81+ await gmApiMockServer . close ( ) ;
82+ } ) ;
83+
84+ function patchCode ( code : string ) : string {
85+ return patchGMApiTestCode ( code , gmApiMockServer . origin ) ;
86+ }
87+
888 // Two-phase launch + script install + network fetches + permission dialogs
989 testWithUserScripts . setTimeout ( 300_000 ) ;
1090
1191 testWithUserScripts ( "GM_ sync API tests (gm_api_test.js)" , async ( { context, extensionId } ) => {
12- const { passed, failed, logs } = await runTestScript ( context , extensionId , "gm_api_test.js" , TARGET_URL , 90_000 ) ;
92+ const { passed, failed, logs } = await runTestScript ( context , extensionId , "gm_api_test.js" , TARGET_URL , 90_000 , {
93+ patchCode,
94+ } ) ;
1395
1496 console . log ( `[gm_api_test] passed=${ passed } , failed=${ failed } ` ) ;
1597 if ( failed !== 0 ) {
@@ -25,7 +107,8 @@ testWithUserScripts.describe("GM API", () => {
25107 extensionId ,
26108 "gm_api_async_test.js" ,
27109 TARGET_URL ,
28- 90_000
110+ 90_000 ,
111+ { patchCode }
29112 ) ;
30113
31114 console . log ( `[gm_api_async_test] passed=${ passed } , failed=${ failed } ` ) ;
0 commit comments