@@ -648,4 +648,114 @@ describe("plugins publish route", () => {
648648 screen . getByRole ( "button" , { name : "Publish plugin" } ) . getAttribute ( "disabled" ) ,
649649 ) . not . toBeNull ( ) ;
650650 } ) ;
651+
652+ it ( "warns when README references relative image paths but no source repo/commit is set" , async ( ) => {
653+ renderPublishRoute ( ) ;
654+
655+ const packageJson = withRelativePath (
656+ new File (
657+ [ makeCodePluginPackageJson ( { name : "demo-plugin" , version : "1.0.0" } ) ] ,
658+ "package.json" ,
659+ {
660+ type : "application/json" ,
661+ } ,
662+ ) ,
663+ "demo-plugin/package.json" ,
664+ ) ;
665+ const manifest = withRelativePath (
666+ new File ( [ '{"id":"demo.plugin"}' ] , "openclaw.plugin.json" , { type : "application/json" } ) ,
667+ "demo-plugin/openclaw.plugin.json" ,
668+ ) ;
669+ const readme = withRelativePath (
670+ new File (
671+ [ '# Demo Plugin\n\n\n\n<img src="./images/bar.png" alt="x"/>' ] ,
672+ "README.md" ,
673+ { type : "text/markdown" } ,
674+ ) ,
675+ "demo-plugin/README.md" ,
676+ ) ;
677+
678+ fireEvent . change ( getFileInput ( ) , { target : { files : [ packageJson , manifest , readme ] } } ) ;
679+
680+ await waitFor ( ( ) => {
681+ expect ( screen . getByText ( / Y o u r R E A D M E r e f e r e n c e s 2 r e l a t i v e i m a g e p a t h s / i) ) . toBeTruthy ( ) ;
682+ } ) ;
683+ expect ( screen . getByText ( / C l a w H u b d o e s n o t \s + h o s t p a c k a g e b i n a r y a s s e t s / i) ) . toBeTruthy ( ) ;
684+ expect ( screen . getByText ( / \. \/ i m a g e s \/ f o o \. p n g / ) ) . toBeTruthy ( ) ;
685+ expect ( screen . getByText ( / \. \/ i m a g e s \/ b a r \. p n g / ) ) . toBeTruthy ( ) ;
686+ } ) ;
687+
688+ it ( "hides the relative-README-image warning once Source repo and Source commit are filled" , async ( ) => {
689+ renderPublishRoute ( ) ;
690+
691+ const packageJson = withRelativePath (
692+ new File (
693+ [ makeCodePluginPackageJson ( { name : "demo-plugin" , version : "1.0.0" } ) ] ,
694+ "package.json" ,
695+ {
696+ type : "application/json" ,
697+ } ,
698+ ) ,
699+ "demo-plugin/package.json" ,
700+ ) ;
701+ const manifest = withRelativePath (
702+ new File ( [ '{"id":"demo.plugin"}' ] , "openclaw.plugin.json" , { type : "application/json" } ) ,
703+ "demo-plugin/openclaw.plugin.json" ,
704+ ) ;
705+ const readme = withRelativePath (
706+ new File ( [ "# Demo\n\n\n" ] , "README.md" , {
707+ type : "text/markdown" ,
708+ } ) ,
709+ "demo-plugin/README.md" ,
710+ ) ;
711+
712+ fireEvent . change ( getFileInput ( ) , { target : { files : [ packageJson , manifest , readme ] } } ) ;
713+
714+ await waitFor ( ( ) => {
715+ expect ( screen . getByText ( / Y o u r R E A D M E r e f e r e n c e s a r e l a t i v e i m a g e p a t h / i) ) . toBeTruthy ( ) ;
716+ } ) ;
717+
718+ fireEvent . change ( screen . getByPlaceholderText ( "Source repo (owner/repo)" ) , {
719+ target : { value : "openclaw/demo-plugin" } ,
720+ } ) ;
721+ fireEvent . change ( screen . getByPlaceholderText ( "Source commit" ) , {
722+ target : { value : "abc123" } ,
723+ } ) ;
724+
725+ await waitFor ( ( ) => {
726+ expect ( screen . queryByText ( / Y o u r R E A D M E r e f e r e n c e s / i) ) . toBeNull ( ) ;
727+ } ) ;
728+ } ) ;
729+
730+ it ( "does not warn when README only uses absolute image URLs" , async ( ) => {
731+ renderPublishRoute ( ) ;
732+
733+ const packageJson = withRelativePath (
734+ new File (
735+ [ makeCodePluginPackageJson ( { name : "demo-plugin" , version : "1.0.0" } ) ] ,
736+ "package.json" ,
737+ {
738+ type : "application/json" ,
739+ } ,
740+ ) ,
741+ "demo-plugin/package.json" ,
742+ ) ;
743+ const manifest = withRelativePath (
744+ new File ( [ '{"id":"demo.plugin"}' ] , "openclaw.plugin.json" , { type : "application/json" } ) ,
745+ "demo-plugin/openclaw.plugin.json" ,
746+ ) ;
747+ const readme = withRelativePath (
748+ new File ( [ "# Demo\n\n\n" ] , "README.md" , {
749+ type : "text/markdown" ,
750+ } ) ,
751+ "demo-plugin/README.md" ,
752+ ) ;
753+
754+ fireEvent . change ( getFileInput ( ) , { target : { files : [ packageJson , manifest , readme ] } } ) ;
755+
756+ await waitFor ( ( ) => {
757+ expect ( screen . getByDisplayValue ( "demo-plugin" ) ) . toBeTruthy ( ) ;
758+ } ) ;
759+ expect ( screen . queryByText ( / Y o u r R E A D M E r e f e r e n c e s / i) ) . toBeNull ( ) ;
760+ } ) ;
651761} ) ;
0 commit comments