@@ -4045,6 +4045,51 @@ TEST(extract_blazor_component_without_page_has_no_route) {
40454045 PASS ();
40464046}
40474047
4048+ /* Razor Pages: `@page` is what turns a .cshtml view INTO a page — it is the
4049+ * defining directive of the model, not an optional annotation as it is on a
4050+ * Blazor component. So an ASP.NET Core app's routable surface lives entirely
4051+ * in file types that were unmapped until now, and every one of those routes
4052+ * was invisible.
4053+ *
4054+ * Same mechanism as the .razor case: the directive sits in markup above any
4055+ * code block, where the C# grammar never reaches, so it is read from raw
4056+ * source and hangs off the file's Module definition. */
4057+ TEST (extract_razor_page_directive_routes_cshtml_view ) {
4058+ CBMFileResult * r = extract ("@page \"/orders\"\n"
4059+ "@model OrderIndexModel\n"
4060+ "\n"
4061+ "<h1>Orders</h1>\n"
4062+ "<table><tr><td>@Model.Count</td></tr></table>\n" ,
4063+ CBM_LANG_CSHARP , "t" , "Pages/Orders/Index.cshtml" );
4064+ ASSERT_NOT_NULL (r );
4065+ const CBMDefinition * mod = find_module_def (r );
4066+ ASSERT_NOT_NULL (mod );
4067+ ASSERT_NOT_NULL (mod -> route_path );
4068+ ASSERT_STR_EQ (mod -> route_path , "/orders" );
4069+ /* A Razor Page is reached by navigation, i.e. GET — same as a component. */
4070+ ASSERT_NOT_NULL (mod -> route_method );
4071+ ASSERT_STR_EQ (mod -> route_method , "GET" );
4072+ cbm_free_result (r );
4073+ PASS ();
4074+ }
4075+
4076+ /* The overwhelming majority of .cshtml files are layouts, partials and views
4077+ * with no `@page` at all. If the scan fired on those, an ASP.NET app would
4078+ * gain a bogus Route node per view — worse than the missing routes it set out
4079+ * to fix, because a wrong route looks authoritative. */
4080+ TEST (extract_razor_layout_without_page_has_no_route ) {
4081+ CBMFileResult * r = extract ("@model LayoutModel\n"
4082+ "<!DOCTYPE html>\n"
4083+ "<html><body>@RenderBody()</body></html>\n" ,
4084+ CBM_LANG_CSHARP , "t" , "Pages/Shared/_Layout.cshtml" );
4085+ ASSERT_NOT_NULL (r );
4086+ const CBMDefinition * mod = find_module_def (r );
4087+ ASSERT_NOT_NULL (mod );
4088+ ASSERT_NULL (mod -> route_path );
4089+ cbm_free_result (r );
4090+ PASS ();
4091+ }
4092+
40484093/* A comment between decorators must not drop the decorators above it.
40494094 * Comments are NAMED tree-sitter nodes, so the prev-sibling walk used to stop
40504095 * at one — a documented route (@Post + @HttpCode above an explanatory comment)
@@ -7208,6 +7253,8 @@ SUITE(extraction) {
72087253 RUN_TEST (extract_java_jaxrs_path_composition_issue1005 );
72097254 RUN_TEST (extract_blazor_page_directive_routes_component );
72107255 RUN_TEST (extract_blazor_component_without_page_has_no_route );
7256+ RUN_TEST (extract_razor_page_directive_routes_cshtml_view );
7257+ RUN_TEST (extract_razor_layout_without_page_has_no_route );
72117258 RUN_TEST (extract_ts_template_string_url_issue1006 );
72127259 RUN_TEST (extract_go_binary_concat_url_issue1249 );
72137260 RUN_TEST (extract_go_binary_concat_url_no_literal_suffix_issue1249 );
0 commit comments