@@ -60,9 +60,14 @@ public static void TriggerEvent(this IElement element, string eventName, EventAr
6060 public static Task TriggerEventAsync ( this IElement element , string eventName , EventArgs eventArgs )
6161 {
6262 if ( element is null )
63+ {
6364 throw new ArgumentNullException ( nameof ( element ) ) ;
65+ }
66+
6467 if ( eventName is null )
68+ {
6569 throw new ArgumentNullException ( nameof ( eventName ) ) ;
70+ }
6671
6772 var renderer = element . GetTestContext ( ) ? . Renderer
6873 ?? throw new InvalidOperationException ( $ "Blazor events can only be raised on elements rendered with the Blazor test renderer '{ nameof ( ITestRenderer ) } '.") ;
@@ -80,49 +85,41 @@ public static Task TriggerEventAsync(this IElement element, string eventName, Ev
8085 }
8186
8287 [ SuppressMessage ( "Globalization" , "CA1308:Normalize strings to uppercase" , Justification = "HTML events are standardize to lower case and safe in this context." ) ]
83- private static async Task TriggerEventsAsync ( ITestRenderer renderer , IElement element , string eventName , EventArgs eventArgs )
88+ private static Task TriggerEventsAsync ( ITestRenderer renderer , IElement element , string eventName , EventArgs eventArgs )
8489 {
8590 var isNonBubblingEvent = NonBubblingEvents . Contains ( eventName . ToLowerInvariant ( ) ) ;
8691 var unwrappedElement = element . Unwrap ( ) ;
87- if ( isNonBubblingEvent )
88- await TriggerNonBubblingEventAsync ( renderer , unwrappedElement , eventName , eventArgs ) ;
89- else
90- await TriggerBubblingEventAsync ( renderer , unwrappedElement , eventName , eventArgs ) ;
9192
92- switch ( unwrappedElement )
93- {
94- case IHtmlInputElement { Type : "submit" , Form : not null } input when eventName is "onclick" :
95- await TriggerFormSubmitAsync ( renderer , input , eventArgs , input . Form ) ;
96- break ;
97- case IHtmlButtonElement { Type : "submit" , Form : not null } button when eventName is "onclick" :
98- await TriggerFormSubmitAsync ( renderer , button , eventArgs , button . Form ) ;
99- break ;
100- }
93+ return isNonBubblingEvent
94+ ? TriggerNonBubblingEventAsync ( renderer , unwrappedElement , eventName , eventArgs )
95+ : TriggerBubblingEventAsync ( renderer , unwrappedElement , eventName , eventArgs ) ;
10196 }
10297
103- private static Task TriggerFormSubmitAsync ( ITestRenderer renderer , IElement element , EventArgs eventArgs , IHtmlFormElement form )
98+ private static Task TriggerNonBubblingEventAsync ( ITestRenderer renderer , IElement element , string eventName , EventArgs eventArgs )
10499 {
105- const string eventName = "onclick" ;
106-
107100 var eventAttrName = Htmlizer . ToBlazorAttribute ( eventName ) ;
108- var preventDefaultAttrName = $ "{ eventAttrName } :preventdefault";
109- if ( element . HasAttribute ( preventDefaultAttrName ) )
110- return Task . CompletedTask ;
111101
112- var events = GetDispatchEventTasks ( renderer , form , "onsubmit" , eventArgs ) ;
102+ if ( string . Equals ( eventName , "onsubmit" , StringComparison . Ordinal ) && element is not IHtmlFormElement )
103+ {
104+ throw new InvalidOperationException ( "Only forms can have a onsubmit event" ) ;
105+ }
113106
114- if ( events . Count == 0 )
115- throw new MissingEventHandlerException ( element , eventName ) ;
107+ if ( element . TryGetEventId ( eventAttrName , out var id ) )
108+ {
109+ return renderer . DispatchEventAsync ( id , new EventFieldInfo { FieldValue = eventName } , eventArgs ) ;
110+ }
116111
117- return Task . WhenAll ( events ) ;
112+ throw new MissingEventHandlerException ( element , eventName ) ;
118113 }
119114
120115 private static Task TriggerBubblingEventAsync ( ITestRenderer renderer , IElement element , string eventName , EventArgs eventArgs )
121116 {
122117 var eventTasks = GetDispatchEventTasks ( renderer , element , eventName , eventArgs ) ;
123118
124119 if ( eventTasks . Count == 0 )
120+ {
125121 throw new MissingEventHandlerException ( element , eventName ) ;
122+ }
126123
127124 return Task . WhenAll ( eventTasks ) ;
128125 }
@@ -139,10 +136,17 @@ private static List<Task> GetDispatchEventTasks(
139136
140137 foreach ( var candidate in element . GetParentsAndSelf ( ) )
141138 {
142- if ( candidate . TryGetEventId ( eventAttrName , out var id ) )
139+ if ( candidate . TryGetEventId ( eventAttrName , out var eventId ) )
143140 {
144141 var info = new EventFieldInfo { FieldValue = eventName } ;
145- eventTasks . Add ( renderer . DispatchEventAsync ( id , info , eventArgs , ignoreUnknownEventHandlers : eventTasks . Count > 0 ) ) ;
142+ eventTasks . Add ( renderer . DispatchEventAsync ( eventId , info , eventArgs , ignoreUnknownEventHandlers : eventTasks . Count > 0 ) ) ;
143+ }
144+
145+ // Special case for elements inside form elements
146+ if ( TryGetParentFormElementSpecialCase ( candidate , eventName , out var formEventId ) )
147+ {
148+ var info = new EventFieldInfo { FieldValue = "onsubmit" } ;
149+ eventTasks . Add ( renderer . DispatchEventAsync ( formEventId , info , eventArgs , ignoreUnknownEventHandlers : true ) ) ;
146150 }
147151
148152 if ( candidate . HasAttribute ( eventStopPropagationAttrName ) || candidate . EventIsDisabled ( eventName ) )
@@ -154,34 +158,46 @@ private static List<Task> GetDispatchEventTasks(
154158 return eventTasks ;
155159 }
156160
157- private static Task TriggerNonBubblingEventAsync ( ITestRenderer renderer , IElement element , string eventName , EventArgs eventArgs )
161+ private static bool TryGetParentFormElementSpecialCase (
162+ IElement element ,
163+ string eventName ,
164+ out ulong eventId )
158165 {
159- var eventAttrName = Htmlizer . ToBlazorAttribute ( eventName ) ;
166+ eventId = default ;
160167
161- if ( string . Equals ( eventName , "onsubmit" , StringComparison . Ordinal ) && element is not IHtmlFormElement )
162- throw new InvalidOperationException ( "Only forms can have a onsubmit event" ) ;
168+ if ( ! eventName . Equals ( "onclick" , StringComparison . OrdinalIgnoreCase ) )
169+ {
170+ return false ;
171+ }
163172
164- if ( element . TryGetEventId ( eventAttrName , out var id ) )
165- return renderer . DispatchEventAsync ( id , new EventFieldInfo { FieldValue = eventName } , eventArgs ) ;
173+ var eventAttrName = Htmlizer . ToBlazorAttribute ( eventName ) ;
174+ var preventDefaultAttrName = $ "{ eventAttrName } :preventdefault";
175+ if ( element . HasAttribute ( preventDefaultAttrName ) )
176+ {
177+ return false ;
178+ }
166179
167- throw new MissingEventHandlerException ( element , eventName ) ;
180+ var form = element switch
181+ {
182+ IHtmlInputElement { Type : "submit" , Form : not null } input => input . Form ,
183+ IHtmlButtonElement { Type : "submit" , Form : not null } button => button . Form ,
184+ _ => null
185+ } ;
186+
187+ return form is not null
188+ && form . TryGetEventId ( Htmlizer . ToBlazorAttribute ( "onsubmit" ) , out eventId ) ;
168189 }
169190
170191 private static bool EventIsDisabled ( this IElement element , string eventName )
171192 {
172193 // We want to replicate the normal DOM event behavior that, for 'interactive' elements
173194 // with a 'disabled' attribute, certain mouse events are suppressed
174195
175- switch ( element )
196+ return element switch
176197 {
177- case IHtmlButtonElement :
178- case IHtmlInputElement :
179- case IHtmlTextAreaElement :
180- case IHtmlSelectElement :
181- return DisabledEventNames . Contains ( eventName ) && element . IsDisabled ( ) ;
182- default :
183- return false ;
184- }
198+ IHtmlButtonElement or IHtmlInputElement or IHtmlTextAreaElement or IHtmlSelectElement => DisabledEventNames . Contains ( eventName ) && element . IsDisabled ( ) ,
199+ _ => false ,
200+ } ;
185201 }
186202
187203 private static bool TryGetEventId ( this IElement element , string blazorEventName , out ulong id )
0 commit comments