@@ -723,11 +723,11 @@ describe("ScriptClient 站点范围消息", () => {
723723 const sendMessage = vi . fn ( ) . mockResolvedValue ( { data : true } ) ;
724724 const client = new ScriptClient ( { sendMessage } as unknown as MessageSend ) ;
725725
726- await client . excludeFromMatch ( "script-uuid" , "* ://current.example/* " ) ;
726+ await client . excludeFromMatch ( "script-uuid" , "current.example" , "https ://current.example/page ") ;
727727
728728 expect ( sendMessage ) . toHaveBeenCalledWith ( {
729729 action : "serviceWorker/script/excludeFromMatch" ,
730- data : { uuid : "script-uuid" , matchPattern : "*: //current.example/* " } ,
730+ data : { uuid : "script-uuid" , host : "current.example" , url : "https: //current.example/page " } ,
731731 } ) ;
732732 } ) ;
733733} ) ;
@@ -786,51 +786,10 @@ describe("ScriptService selfMetadata 用户覆盖", () => {
786786 ) ;
787787 } ) ;
788788
789- describe ( "excludeUrl - popup 排除/取消排除" , ( ) => {
790- it ( "取消最后一条排除后应保存空覆盖,而不是回落脚本自带的 exclude" , async ( ) => {
791- const script = createMockScript ( ) ;
792- vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
793-
794- await scriptService . excludeUrl ( { uuid : script . uuid , excludePattern : "*://ads.script.com/*" , remove : true } ) ;
795-
796- expect ( savedSelfMetadata ( ) ) . toEqual ( { exclude : [ ] } ) ;
797- } ) ;
798-
799- it ( "取消排除后仍有其他规则时应保存剩余规则" , async ( ) => {
800- const script = createMockScript ( {
801- selfMetadata : { exclude : [ "*://ads.script.com/*" , "*://user.com/*" ] } ,
802- } ) ;
803- vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
804-
805- await scriptService . excludeUrl ( { uuid : script . uuid , excludePattern : "*://user.com/*" , remove : true } ) ;
806-
807- expect ( savedSelfMetadata ( ) ) . toEqual ( { exclude : [ "*://ads.script.com/*" ] } ) ;
808- } ) ;
809-
810- it ( "排除新网站时应追加到覆盖中" , async ( ) => {
811- const script = createMockScript ( ) ;
812- vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
813-
814- await scriptService . excludeUrl ( { uuid : script . uuid , excludePattern : "*://user.com/*" , remove : false } ) ;
815-
816- expect ( savedSelfMetadata ( ) ) . toEqual ( { exclude : [ "*://ads.script.com/*" , "*://user.com/*" ] } ) ;
817- } ) ;
818-
819- it ( "已有用户排除覆盖时新增排除应同时保留作者与用户规则" , async ( ) => {
820- const script = createMockScript ( {
821- selfMetadata : { exclude : [ "*://user-blocked.example/*" ] } ,
822- } ) ;
823- vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
824-
825- await scriptService . excludeUrl ( { uuid : script . uuid , excludePattern : "*://new.example/*" , remove : false } ) ;
826-
827- expect ( savedSelfMetadata ( ) ) . toEqual ( {
828- exclude : [ "*://ads.script.com/*" , "*://user-blocked.example/*" , "*://new.example/*" ] ,
829- } ) ;
830- } ) ;
831- } ) ;
832-
833789 describe ( "popup 站点范围快捷操作" , ( ) => {
790+ const host = "current.example" ;
791+ const url = "https://current.example/page" ;
792+
834793 it ( "初始化时应注册排除已匹配站点操作" , async ( ) => {
835794 const alarmsDescriptor = Object . getOwnPropertyDescriptor ( chrome , "alarms" ) ;
836795 Object . defineProperty ( chrome , "alarms" , {
@@ -894,17 +853,14 @@ describe("ScriptService selfMetadata 用户覆盖", () => {
894853
895854 const onlyRun = scriptService . onlyRunOnUrl ( { uuid : stored . uuid , matchPattern : "*://current.example/*" } ) ;
896855 await firstUpdateStarted ;
897- const exclude = scriptService . excludeFromMatch ( { uuid : stored . uuid , matchPattern : "*://current.example/*" } ) ;
856+ const exclude = scriptService . excludeFromMatch ( { uuid : stored . uuid , host , url } ) ;
898857 await Promise . resolve ( ) ;
899858 await Promise . resolve ( ) ;
900859 releaseFirstUpdate ( ) ;
901860 await Promise . all ( [ onlyRun , exclude ] ) ;
902861
903- expect ( stored . selfMetadata ) . toEqual ( {
904- match : [ ] ,
905- include : [ ] ,
906- exclude : [ "*://ads.script.com/*" , "*://current.example/*" ] ,
907- } ) ;
862+ // 串行执行才能让 excludeFromMatch 看到 onlyRunOnUrl 写入的匹配覆盖并把它移出
863+ expect ( stored . selfMetadata ) . toEqual ( { match : [ ] , include : [ ] } ) ;
908864 } ) ;
909865
910866 it ( "并发 onlyRunOnUrl 与 resetMatch 应串行执行并保留两次读改写" , async ( ) => {
@@ -1012,86 +968,118 @@ describe("ScriptService selfMetadata 用户覆盖", () => {
1012968 } ) ;
1013969 } ) ;
1014970
1015- it ( "排除已包含站点时应移出用户匹配并加入用户排除 " , async ( ) => {
971+ it ( "匹配中有当前站点的专属规则时应只移出匹配,不写入排除 " , async ( ) => {
1016972 const script = createMockScript ( {
1017- selfMetadata : {
1018- match : [ "*://allowed.example/*" , "*://current.example/*" ] ,
1019- exclude : [ "*://blocked.example/*" ] ,
1020- } ,
973+ metadata : { match : [ "*://current.example/*" , "*://other.example/*" ] } ,
974+ selfMetadata : { exclude : [ "*://blocked.example/*" ] } ,
1021975 } ) ;
1022976 vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
1023977
1024- await scriptService . excludeFromMatch ( { uuid : script . uuid , matchPattern : "*://current.example/*" } ) ;
978+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host , url } ) ;
1025979
1026- // 作者 @exclude(ads.script.com)并入用户覆盖,避免用户覆盖整体替换作者规则
980+ // 移出匹配后脚本已不在本站生效,无需再写排除,用户的排除列表保持原样
1027981 expect ( savedSelfMetadata ( ) ) . toEqual ( {
1028- match : [ "*://allowed .example/*" ] ,
1029- exclude : [ "*://ads.script.com/*" , "*:// blocked.example/*" , "*://current .example/*"] ,
982+ match : [ "*://other .example/*" ] ,
983+ exclude : [ "*://blocked.example/*" ] ,
1030984 } ) ;
1031985 } ) ;
1032986
1033- it ( "排除最后一个用户匹配时应保留显式空匹配覆盖" , async ( ) => {
1034- const script = createMockScript ( { selfMetadata : { match : [ "*://current.example/*" ] } } ) ;
987+ it ( "同一站点的多条路径匹配应一并移出" , async ( ) => {
988+ const script = createMockScript ( {
989+ metadata : { match : [ "https://current.example/a*" , "http://current.example/b*" , "*://other.example/*" ] } ,
990+ } ) ;
1035991 vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
1036992
1037- await scriptService . excludeFromMatch ( { uuid : script . uuid , matchPattern : "*://current.example/*" } ) ;
993+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host , url } ) ;
1038994
1039- expect ( savedSelfMetadata ( ) ) . toEqual ( {
1040- match : [ ] ,
1041- exclude : [ "*://ads.script.com/*" , "*://current.example/*" ] ,
1042- } ) ;
995+ expect ( savedSelfMetadata ( ) ) . toEqual ( { match : [ "*://other.example/*" ] } ) ;
1043996 } ) ;
1044997
1045- it ( "没有用户匹配覆盖时排除站点不应创建匹配覆盖 " , async ( ) => {
1046- const script = createMockScript ( ) ;
998+ it ( "移出的是最后一条匹配时应保留显式空匹配覆盖 " , async ( ) => {
999+ const script = createMockScript ( { metadata : { match : [ "*://current.example/*" ] } } ) ;
10471000 vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
10481001
1049- await scriptService . excludeFromMatch ( { uuid : script . uuid , matchPattern : "*://current.example/*" } ) ;
1002+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host , url } ) ;
10501003
1051- expect ( savedSelfMetadata ( ) ) . toEqual ( { exclude : [ "*://ads.script.com/*" , "*://current.example/*" ] } ) ;
1004+ expect ( savedSelfMetadata ( ) ) . toEqual ( { match : [ ] } ) ;
10521005 } ) ;
10531006
1054- it ( "已有空匹配覆盖时排除站点应保留空覆盖 " , async ( ) => {
1055- const script = createMockScript ( { selfMetadata : { match : [ ] } } ) ;
1007+ it ( "通配匹配移不掉当前站点时应回退为写入排除 " , async ( ) => {
1008+ const script = createMockScript ( { metadata : { match : [ "*://*/*" ] } } ) ;
10561009 vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
10571010
1058- await scriptService . excludeFromMatch ( { uuid : script . uuid , matchPattern : "*://current.example/*" } ) ;
1011+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host , url } ) ;
10591012
1060- expect ( savedSelfMetadata ( ) ) . toEqual ( {
1061- match : [ ] ,
1062- exclude : [ "*://ads.script.com/*" , "*://current.example/*" ] ,
1013+ // 通配匹配删不掉单一站点,只有排除能真正关掉;同时不该创建匹配覆盖
1014+ expect ( savedSelfMetadata ( ) ) . toEqual ( { exclude : [ "*://current.example/*" ] } ) ;
1015+ } ) ;
1016+
1017+ it ( "不应移除通配子域匹配,改以排除关掉当前子域" , async ( ) => {
1018+ const script = createMockScript ( { metadata : { match : [ "*://*.example.com/*" ] } } ) ;
1019+ vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
1020+
1021+ // 移除 *://*.example.com/* 会连兄弟子域一起关掉,超出「不在 www.example.com 执行」的范围
1022+ await scriptService . excludeFromMatch ( {
1023+ uuid : script . uuid ,
1024+ host : "www.example.com" ,
1025+ url : "https://www.example.com/page" ,
10631026 } ) ;
1027+
1028+ expect ( savedSelfMetadata ( ) ) . toEqual ( { exclude : [ "*://www.example.com/*" ] } ) ;
10641029 } ) ;
10651030
1066- it ( "新增排除覆盖时应保留作者已有的排除规则 " , async ( ) => {
1031+ it ( "@include 仍命中当前站点时应在移出匹配后补写排除 " , async ( ) => {
10671032 const script = createMockScript ( {
1068- metadata : { exclude : [ "*://author-blocked.example/*" ] } ,
1069- selfMetadata : { match : [ "*://current.example/*" ] } ,
1033+ metadata : { match : [ "*://current.example/*" ] , include : [ "*://current.example/*" ] } ,
10701034 } ) ;
10711035 vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
10721036
1073- await scriptService . excludeFromMatch ( { uuid : script . uuid , matchPattern : "*://current.example/*" } ) ;
1037+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host , url } ) ;
10741038
10751039 expect ( savedSelfMetadata ( ) ) . toEqual ( {
10761040 match : [ ] ,
1077- exclude : [ "*://author-blocked.example/*" , "*:// current.example/*"] ,
1041+ exclude : [ "*://current.example/*" ] ,
10781042 } ) ;
10791043 } ) ;
10801044
1081- it ( "已有用户排除覆盖时排除站点应同时保留作者与用户排除规则 " , async ( ) => {
1045+ it ( "仅在当前站点执行后再关掉当前站点应清空匹配并撤销来源标记 " , async ( ) => {
10821046 const script = createMockScript ( {
1083- metadata : { exclude : [ "*://author-blocked.example/*" ] } ,
1084- selfMetadata : { match : [ "*://current.example/*" ] , exclude : [ "*://user-blocked.example/*" ] } ,
1047+ selfMetadata : {
1048+ match : [ "*://current.example/*" ] ,
1049+ include : [ ] ,
1050+ [ SELF_METADATA_ONLY_RUN_ON_URL ] : [ "*://current.example/*" ] ,
1051+ } ,
1052+ } ) ;
1053+ vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
1054+
1055+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host, url } ) ;
1056+
1057+ expect ( savedSelfMetadata ( ) ) . toEqual ( { match : [ ] , include : [ ] } ) ;
1058+ } ) ;
1059+
1060+ it ( "写入排除时应同时保留作者与用户已有的排除规则" , async ( ) => {
1061+ const script = createMockScript ( {
1062+ metadata : { match : [ "*://*/*" ] , exclude : [ "*://author-blocked.example/*" ] } ,
1063+ selfMetadata : { exclude : [ "*://user-blocked.example/*" ] } ,
10851064 } ) ;
10861065 vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
10871066
1088- await scriptService . excludeFromMatch ( { uuid : script . uuid , matchPattern : "*://current.example/*" } ) ;
1067+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host , url } ) ;
10891068
1069+ // 用户覆盖整体替换作者规则,因此写排除时须并入作者 @exclude,避免丢作者规则
10901070 expect ( savedSelfMetadata ( ) ) . toEqual ( {
1091- match : [ ] ,
10921071 exclude : [ "*://author-blocked.example/*" , "*://user-blocked.example/*" , "*://current.example/*" ] ,
10931072 } ) ;
10941073 } ) ;
1074+
1075+ it ( "当前站点本就不在匹配范围内时不应写入任何覆盖" , async ( ) => {
1076+ const script = createMockScript ( { metadata : { match : [ "*://other.example/*" ] } } ) ;
1077+ vi . mocked ( mockScriptDAO . get ) . mockResolvedValue ( script ) ;
1078+
1079+ await scriptService . excludeFromMatch ( { uuid : script . uuid , host, url } ) ;
1080+
1081+ expect ( mockScriptDAO . update ) . not . toHaveBeenCalled ( ) ;
1082+ } ) ;
10951083 } ) ;
10961084
10971085 describe ( "resetMatch / resetExclude - 编辑器匹配列表" , ( ) => {
0 commit comments