diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..a2ee9a494 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-serve": { + "version": "1.10.194", + "commands": [ + "dotnet-serve" + ], + "rollForward": false + }, + "docfx": { + "version": "2.78.5", + "commands": [ + "docfx" + ], + "rollForward": false + }, + "dotnet-dump": { + "version": "9.0.661903", + "commands": [ + "dotnet-dump" + ], + "rollForward": false + } + } +} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..41f46fb35 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet +{ + "name": "Full", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/dotnet/sdk:9.0", + "features": { + "ghcr.io/devcontainers/features/dotnet:2": { + "version": "latest", + "additionalVersions": "8.0" + } + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "ms-dotnettools.csdevkit", + "yzhang.markdown-all-in-one", + "vscode-icons-team.vscode-icons", + "me-dutour-mathieu.vscode-github-actions" + ] + } + }, + + "postCreateCommand": "bash .devcontainer/post-install.sh" + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.devcontainer/post-install.sh b/.devcontainer/post-install.sh new file mode 100644 index 000000000..0c990f19e --- /dev/null +++ b/.devcontainer/post-install.sh @@ -0,0 +1,7 @@ +#/bin/bash + +# Install docfx (should be aligned with docs-deploy.yml) +dotnet tool restore + +# Trust dotnet developer certs +dotnet dev-certs https --check --trust diff --git a/.editorconfig b/.editorconfig index b350b6336..1fbb86aae 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,226 +1,513 @@ -# Remove the line below if you want to inherit .editorconfig settings from higher directories root = true -# C# files +[*] +indent_style = tab +indent_size = tab +tab_width = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + [*.cs] +tab_size = 4 + +[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}] +tab_size = 2 + +[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,svg,vue}] +tab_size = 2 + +[*.json] +tab_size = 2 -#### Core EditorConfig Options #### +[*.{ps1,psm1}] +tab_size = 4 -# Indentation and spacing -indent_size = 4 +[*.sh] +tab_size = 4 +end_of_line = lf + +[*.{yml,yaml}] indent_style = space -tab_width = 4 +tab_size = 2 + +[*.md] +trim_trailing_whitespace = false -# New line preferences +[*.{cmd,bat}] end_of_line = crlf -insert_final_newline = false -#### .NET Coding Conventions #### +[Makefile] +indent_style = tab -# Organize usings -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = true +########################################## +# .NET Language Conventions +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions +########################################## -# this. and Me. preferences -dotnet_style_qualification_for_event = false:silent -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_property = false:silent +# Default Severity for .NET Code Style +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#scope +dotnet_analyzer_diagnostic.severity = error -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent +# Misc preferences +file_header_template = unset # IDE0073 +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +# .NET Code Style Settings +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings +[*.{cs,csx,cake}] +# "this." and "Me." qualifiers +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_property = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_event = false + +# Language keywords instead of framework type names for type references +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords +dotnet_style_predefined_type_for_locals_parameters_members = true # IDE0049 +dotnet_style_predefined_type_for_member_access = true # IDE0049 # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers +dotnet_style_require_accessibility_modifiers = always # IDE0040 +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async # IDE0036 +dotnet_style_readonly_field = true # IDE0044 -# Expression-level preferences -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_object_initializer = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_prefer_compound_assignment = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion - -# Field preferences -dotnet_style_readonly_field = true:suggestion +# Parentheses preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity # IDE0047 and IDE0048 +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity # IDE0047 and IDE0048 +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity # IDE0047 and IDE0048 +dotnet_style_parentheses_in_other_operators = always_for_clarity # IDE0047 and IDE0048 -# Parameter preferences -dotnet_code_quality_unused_parameters = all:suggestion +# Expression-level preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences +dotnet_style_object_initializer = true # IDE0017 +dotnet_style_collection_initializer = true # IDE0028 +dotnet_style_explicit_tuple_names = true # IDE0033 +dotnet_style_prefer_inferred_tuple_names = true # IDE0037 +dotnet_style_prefer_inferred_anonymous_type_member_names = true # IDE0037 +dotnet_style_prefer_auto_properties = true # IDE0050 +dotnet_style_prefer_is_null_check_over_reference_equality_method = true # IDE0041 +dotnet_style_prefer_conditional_expression_over_assignment = true # IDE0045 +dotnet_style_prefer_conditional_expression_over_return = true # IDE0046 +dotnet_style_prefer_compound_assignment = true # IDE0054 and IDE0074 +dotnet_style_prefer_simplified_interpolation = true # IDE0071 +dotnet_style_prefer_simplified_boolean_expressions = true # IDE0075 -#### C# Coding Conventions #### +# Null-checking preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences +dotnet_style_coalesce_expression = true # IDE0029 and IDE0030 +dotnet_style_null_propagation = true # IDE0031 -# var preferences -csharp_style_var_elsewhere = true:silent -csharp_style_var_for_built_in_types = true:silent -csharp_style_var_when_type_is_apparent = true:silent +# Parameter preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences +dotnet_code_quality_unused_parameters = all # IDE0060 -# Expression-bodied members -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_constructors = when_on_single_line:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = when_on_single_line:silent -csharp_style_expression_bodied_methods = when_on_single_line:silent -csharp_style_expression_bodied_operators = when_on_single_line:silent -csharp_style_expression_bodied_properties = true:silent - -# Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_prefer_switch_expression = true:suggestion +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none # IDE0079 -# Null-checking preferences -csharp_style_conditional_delegate_call = true:suggestion +# More style options (Undocumented) +# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641 +dotnet_style_operator_placement_when_wrapping = end_of_line -# Modifier preferences -csharp_prefer_static_local_function = true:suggestion -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async +# C# Code Style Settings +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings +[*.{cs,csx,cake}] +# Implicit and explicit types +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types +csharp_style_var_for_built_in_types = true # IDE0007 and IDE0008 +csharp_style_var_when_type_is_apparent = true # IDE0007 and IDE0008 +csharp_style_var_elsewhere = true # IDE0007 and IDE0008 -# Code-block preferences -csharp_prefer_braces = when_multiline:silent -csharp_prefer_simple_using_statement = true:suggestion +# Expression-bodied members +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members +csharp_style_expression_bodied_constructors = when_on_single_line # IDE0021 +csharp_style_expression_bodied_methods = true # IDE0022 +csharp_style_expression_bodied_operators = true # IDE0023 and IDE0024 +csharp_style_expression_bodied_properties = true # IDE0025 +csharp_style_expression_bodied_indexers = true # IDE0026 +csharp_style_expression_bodied_accessors = true # IDE0027 +csharp_style_expression_bodied_lambdas = true # IDE0053 +csharp_style_expression_bodied_local_functions = true # IDE0061 + +# Pattern matching +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching +csharp_style_pattern_matching_over_as_with_null_check = true # IDE0019 +csharp_style_pattern_matching_over_is_with_cast_check = true # IDE0020 +csharp_style_prefer_switch_expression = true:suggestion # IDE0066 +csharp_style_prefer_pattern_matching = true:silent # IDE0078 +csharp_style_prefer_not_pattern = true:suggestion # IDE0083 + +# Inlined variable declarations +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations +csharp_style_inlined_variable_declaration = true # IDE0018 # Expression-level preferences -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_prefer_index_operator = true:suggestion -csharp_style_prefer_range_operator = true:suggestion -csharp_style_throw_expression = true:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:silent - -# 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent - -#### C# Formatting Rules #### - -# New line preferences -csharp_new_line_before_catch = true +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences +csharp_prefer_simple_default_expression = true # IDE0034 + +# "Null" checking preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences +csharp_style_throw_expression = true # IDE0016 +csharp_style_conditional_delegate_call = true # IDE1005 + +# Code block preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences +csharp_prefer_braces = true # IDE0011 + +# Unused value preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences +csharp_style_unused_value_expression_statement_preference = discard_variable # IDE0058 +csharp_style_unused_value_assignment_preference = discard_variable # IDE0059 + +# Index and range preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences +csharp_style_prefer_index_operator = true # IDE0056 +csharp_style_prefer_range_operator = true # IDE0057 + +# Miscellaneous preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences +csharp_style_pattern_local_over_anonymous_function = true # IDE0039 +csharp_style_deconstructed_variable_declaration = true # IDE0042 +csharp_prefer_static_local_function = true # IDE0062 +csharp_prefer_simple_using_statement = true # IDE0063 +csharp_using_directive_placement = outside_namespace # IDE0065 + +########################################## +# .NET Formatting Conventions +# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions +########################################## + +# Newline options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options +csharp_new_line_before_open_brace = all csharp_new_line_before_else = true +csharp_new_line_before_catch = true csharp_new_line_before_finally = true -csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_open_brace = all +csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_between_query_expression_clauses = true -# Indentation preferences +# Indentation options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = no_change csharp_indent_block_contents = true csharp_indent_braces = false -csharp_indent_case_contents = true csharp_indent_case_contents_when_block = false -csharp_indent_labels = no_change -csharp_indent_switch_labels = true -# Space preferences +# Spacing options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_after_comma = true +csharp_space_before_comma = false csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_before_dot = false csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after +csharp_space_before_semicolon_in_for_statement = false csharp_space_around_declaration_statements = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false csharp_space_between_square_brackets = false -# Wrapping preferences +# Wrapping options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options +csharp_preserve_single_line_statements = false csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = true - -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.static_field_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field -dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.const_field_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.const_field_should_be_pascal_case.symbols = const_field -dotnet_naming_rule.const_field_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.private_or_internal_field_should_be_fields_begin_with__.severity = suggestion -dotnet_naming_rule.private_or_internal_field_should_be_fields_begin_with__.symbols = private_or_internal_field -dotnet_naming_rule.private_or_internal_field_should_be_fields_begin_with__.style = fields_begin_with__ - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.static_field.applicable_kinds = field -dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal -dotnet_naming_symbols.static_field.required_modifiers = static - -dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field -dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private -dotnet_naming_symbols.private_or_internal_field.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal -dotnet_naming_symbols.non_field_members.required_modifiers = - -dotnet_naming_symbols.const_field.applicable_kinds = field -dotnet_naming_symbols.const_field.applicable_accessibilities = * -dotnet_naming_symbols.const_field.required_modifiers = const - -# Naming styles - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -dotnet_naming_style.fields_begin_with__.required_prefix = _ -dotnet_naming_style.fields_begin_with__.required_suffix = -dotnet_naming_style.fields_begin_with__.word_separator = -dotnet_naming_style.fields_begin_with__.capitalization = camel_case +csharp_style_namespace_declarations = file_scoped:warning + +########################################## +# .NET Naming Conventions +# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions +########################################## + +[*.{cs,csx,cake}] + +########################################## +# Styles +########################################## + +# camel_case_style - Define the camelCase style +dotnet_naming_style.camel_case_style.capitalization = camel_case +# pascal_case_style - Define the PascalCase style +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# first_upper_style - The first character must start with an upper-case character +dotnet_naming_style.first_upper_style.capitalization = first_word_upper +# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I' +dotnet_naming_style.prefix_interface_with_i_style.capitalization = pascal_case +dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I +# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T' +dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case +dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T +# disallowed_style - Anything that has this style applied is marked as disallowed +dotnet_naming_style.disallowed_style.capitalization = pascal_case +dotnet_naming_style.disallowed_style.required_prefix = +dotnet_naming_style.disallowed_style.required_suffix = +# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file +dotnet_naming_style.internal_error_style.capitalization = pascal_case +dotnet_naming_style.internal_error_style.required_prefix = +dotnet_naming_style.internal_error_style.required_suffix = + +########################################## +# .NET Design Guideline Field Naming Rules +# Naming rules for fields follow the .NET Framework design guidelines +# https://docs.microsoft.com/dotnet/standard/design-guidelines/index +########################################## + +# All public/protected/protected_internal constant fields must be PascalCase +# https://docs.microsoft.com/dotnet/standard/design-guidelines/field +dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal +dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const +dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field +dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group +dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = pascal_case_style +dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning + +# All public/protected/protected_internal static readonly fields must be PascalCase +# https://docs.microsoft.com/dotnet/standard/design-guidelines/field +dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal +dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly +dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field +dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group +dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style +dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning + +# No other public/protected/protected_internal fields are allowed +# https://docs.microsoft.com/dotnet/standard/design-guidelines/field +dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal +dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field +dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group +dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style +dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error + +########################################## +# StyleCop Field Naming Rules +# Naming rules for fields follow the StyleCop analyzers +# This does not override any rules using disallowed_style above +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers +########################################## + +# All constant fields must be PascalCase +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md +dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private +dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const +dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field +dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group +dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style +dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning + +# All static readonly fields must be PascalCase +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md +dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private +dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly +dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field +dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group +dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style +dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning + +# No non-private instance fields are allowed +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md +dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected +dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field +dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group +dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style +dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error + +# Private fields must be camelCase +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md +dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities = private +dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field +dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group +dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = camel_case_style +dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning + +# Local variables must be camelCase +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md +dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local +dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local +dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group +dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style +dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent + +# This rule should never fire. However, it's included for at least two purposes: +# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers. +# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#). +dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = * +dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field +dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group +dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style +dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error + +########################################## +# Other Naming Rules +########################################## + +# All of the following must be PascalCase: +# - Namespaces +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md +# - Classes and Enumerations +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md +# - Delegates +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types +# - Constructors, Properties, Events, Methods +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members +dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property +dotnet_naming_rule.element_rule.symbols = element_group +dotnet_naming_rule.element_rule.style = pascal_case_style +dotnet_naming_rule.element_rule.severity = warning + +# Interfaces use PascalCase and are prefixed with uppercase 'I' +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces +dotnet_naming_symbols.interface_group.applicable_kinds = interface +dotnet_naming_rule.interface_rule.symbols = interface_group +dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style +dotnet_naming_rule.interface_rule.severity = warning + +# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T' +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces +dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter +dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group +dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style +dotnet_naming_rule.type_parameter_rule.severity = warning + +# Function parameters use camelCase +# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters +dotnet_naming_symbols.parameters_group.applicable_kinds = parameter +dotnet_naming_rule.parameters_rule.symbols = parameters_group +dotnet_naming_rule.parameters_rule.style = camel_case_style +dotnet_naming_rule.parameters_rule.severity = warning + +########################################## + + +########################################## +# Code Analyzers Rules +########################################## + +# AsyncFixer +# http://www.asyncfixer.com + + +# Meziantou +# https://www.meziantou.net/enforcing-asynchronous-code-good-practices-using-a-roslyn-analyzer.htm +dotnet_diagnostic.MA0003.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0003.md +dotnet_diagnostic.MA0004.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0004.md +dotnet_diagnostic.MA0016.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0016.md +dotnet_diagnostic.MA0025.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0025.md +dotnet_diagnostic.MA0026.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0026.md +dotnet_diagnostic.MA0028.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0028.md + + +# Microsoft - Code Analysis +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +dotnet_diagnostic.CA1707.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1707.md +dotnet_diagnostic.CA2007.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA2007.md + + +# SonarAnalyzer.CSharp +# https://rules.sonarsource.com/csharp +dotnet_diagnostic.S1135.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/SonarAnalyzerCSharp/S1135.md + +########################################## +# Custom - Code Analyzers Rules +########################################## + +# Visual Studio +dotnet_diagnostic.IDE0079.severity = warning # IDE0079: Remove unnecessary suppression + +# AsyncFixer + +# Meziantou +dotnet_diagnostic.MA0004.severity = none # MA0004: Use .ConfigureAwait(false) +dotnet_diagnostic.MA0012.severity = none # MA0012: Do not raise reserved exception type +dotnet_diagnostic.MA0015.severity = suggestion # MA0015: Specify the parameter name +dotnet_diagnostic.MA0018.severity = none # MA0018: Do not declare static members on generic types - same as CA1000 + +# Microsoft - Code Analysis +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +dotnet_diagnostic.CA1014.severity = none # CA1014: Mark assemblies with CLSCompliant +dotnet_diagnostic.CA1031.severity = suggestion # CA1031: Do not catch general exception types +dotnet_diagnostic.CA1032.severity = none # CA1032: Implement standard exception constructors +dotnet_diagnostic.CA2201.severity = suggestion # CA2201: Do not raise reserved exception types + +# SonarAnalyzer.CSharp +# https://rules.sonarsource.com/csharp +dotnet_diagnostic.S112.severity = none # S112: General exceptions should never be thrown +dotnet_diagnostic.S1075.severity = suggestion # S1075: URIs should not be hardcoded +dotnet_diagnostic.S1186.severity = suggestion # S1186: Methods should not be empty +dotnet_diagnostic.S2292.severity = suggestion # S2292: Trivial properties should be auto-implemented +dotnet_diagnostic.S3267.severity = suggestion # S3267: Convert foreach loop to LINQ query +dotnet_diagnostic.S2743.severity = suggestion # S2743: A static field in a generic type is not shared among instances +dotnet_diagnostic.S4158.severity = none # BUGGY with C#9 code - doesnt understand local methods + +# Razor specific rules +[*.{cs,razor}] +dotnet_diagnostic.BL0001.severity = none +dotnet_diagnostic.BL0002.severity = none +dotnet_diagnostic.BL0003.severity = none +dotnet_diagnostic.BL0004.severity = none +dotnet_diagnostic.BL0005.severity = none +dotnet_diagnostic.BL0006.severity = none +dotnet_diagnostic.BL0007.severity = none +dotnet_diagnostic.BL0010.severity = none + +########################################## +# Custom Test Code Analyzers Rules +########################################## +[tests/**.cs] + +# Visual Studio +csharp_style_pattern_local_over_anonymous_function = false # IDE0039: Use local function + +# AsyncFixer +# http://www.asyncfixer.com +dotnet_diagnostic.AsyncFixer01.severity = none # AsyncFixer01: Unnecessary async/await usage +dotnet_diagnostic.AsyncFixer04.severity = none # 'cts' is not awaited inside a using block - the resource may be disposed before the async operation completes. Add 'await' to ensure proper resource lifetime. + +# Meziantou +# https://www.meziantou.net/enforcing-asynchronous-code-good-practices-using-a-roslyn-analyzer.htm +dotnet_diagnostic.MA0006.severity = suggestion # MA0006: use String.Equals +dotnet_diagnostic.MA0007.severity = none # MA0007: Add a comma after the last value +dotnet_diagnostic.MA0048.severity = silent # MA0048: File name must match type name + +# Microsoft - Code Analysis +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +dotnet_diagnostic.CA1054.severity = none # CA1054: URI-like parameters should not be strings +dotnet_diagnostic.CA1062.severity = none # CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1707.severity = none # CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1812.severity = none # CA1812: Avoid uninstantiated internal classes - components under test are never instantiated directly. +dotnet_diagnostic.CA1822.severity = suggestion # CA1822: Mark members as static +dotnet_diagnostic.CA1859.severity = none # CA1859: Change return type of method +dotnet_diagnostic.CA2007.severity = none # CA2007: Consider calling ConfigureAwait on the awaited task + +# Microsoft - FxCop +# https://github.com/dotnet/roslyn-analyzers +dotnet_diagnostic.CA1031.severity = none # CA1031: Do not catch general exception types +dotnet_diagnostic.CA2012.severity = none # CA2012: Use ValueTasks correctly +dotnet_diagnostic.CA2201.severity = none # CA2201: Do not raise reserved exception types + +# SonarAnalyzer.CSharp +# https://rules.sonarsource.com/csharp +dotnet_diagnostic.S125.severity = none # S125: Sections of code should not be commented out +dotnet_diagnostic.S3459.severity = none # S3459: Unassigned members should be removed +dotnet_diagnostic.S3871.severity = none # S3871: Exception types should be "public" +dotnet_diagnostic.S1186.severity = none # S1186: Methods should not be empty +dotnet_diagnostic.S1133.severity = none # S1133: Deprecated code should be removed +dotnet_diagnostic.S3963.severity = none # S3963: "static" fields should be initialized inline (covered by CA1810) diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..69a7696f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.verified.cs text eol=lf working-tree-encoding=UTF-8 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 0ecad1474..2e21f81d6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: [egil] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: [bUnit-dev] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] #patreon: # Replace with a single Patreon username #open_collective: # Replace with a single Open Collective username #ko_fi: # Replace with a single Ko-fi username diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 27809d057..14d8ef39c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,13 +4,14 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- -**NOTE:** _Remove any parts of the template you do not need and replace the default text in each section with your own._ + + +**Describe the bug** -**Describe the bug or question** -A clear and concise description of what the bug or question is. + **Example:** Testing this component: @@ -32,11 +33,15 @@ Message output from running the test. ``` **Expected behavior:** -A clear and concise description of what you expected to happen. + + **Version info:** - - Library version: - - .NET Core/Blazor version: + + - bUnit version: + - .NET Runtime and Blazor version: + - OS type and version: **Additional context:** -Add any other context about the problem here. + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..22da1a1c3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Questions and Discussions + url: https://github.com/egil/bUnit/discussions/new + about: Is your issue perhaps more of a discussion or question? Please open topics for discussion or questions using the new GitHub Discussions feature. Remember to choose the right category. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 76afb9053..18919671a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,22 +1,26 @@ --- name: Feature request -about: Suggest an idea for this project +about: Suggest an idea or enhancement for this project title: '' labels: '' assignees: '' --- -**NOTE:** _Remove any parts of the template you do not need and replace the default text in each section with your own._ + -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +**Is the feature request related to a problem? Please elaborate.** -**Describe the solution you'd like** -A clear and concise description of what you want to happen. + -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +**The suggested solution** + + + +**Describe any alternative solutions** + + **Additional context** -Add any other context or screenshots about the feature request here. + + diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 000000000..b79eb6c1a --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,5 @@ +queries: + - uses: security-and-quality + +paths: + - src \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..6abe4ae9d --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,166 @@ +# GitHub Copilot Instructions for bUnit + +This document outlines the coding standards, guidelines, and best practices for contributing to the bUnit project. + +## Project Overview + +bUnit is a testing library for Blazor Components that runs on top of existing unit testing frameworks (xUnit, NUnit, MSTest, TUnit). The goal is to write comprehensive, stable unit tests that run in milliseconds. + +## Coding Standards + +### Language and Framework +- **C# Language Version**: Use `preview` features as defined in `Directory.Build.props` +- **Nullable Reference Types**: Always enabled - use nullable annotations appropriately +- **Implicit Usings**: Enabled - common namespaces are automatically imported + +### Code Style (Based on .editorconfig) + +#### Indentation and Formatting +- Use **tabs** for indentation (not spaces) +- Tab width: 4 spaces +- Charset: UTF-8 +- Always trim trailing whitespace (except in .md files) +- Insert final newline in all files + +#### C# Style Conventions +- Use `var` for built-in types and when type is apparent +- Prefer expression-bodied members for single-line methods, properties, and accessors +- Use pattern matching over `as` with null check and `is` with cast check +- Prefer object and collection initializers +- Use language keywords instead of framework type names (e.g., `int` not `Int32`) +- Always use braces for code blocks (even single-line if statements) +- Prefer `null` propagation and coalesce expressions +- Use file-scoped namespaces (C# 10+) +- Place using directives outside namespace + +#### Naming Conventions +- **PascalCase**: Classes, interfaces (with `I` prefix), methods, properties, events, enums, delegates, namespaces +- **camelCase**: Private fields, local variables, parameters +- **Static readonly fields**: PascalCase +- **Constants**: PascalCase +- **Generic type parameters**: PascalCase with `T` prefix +- **No public/protected instance fields** - use properties instead + +#### Modifiers +- Always specify accessibility modifiers explicitly +- Order: `public`, `private`, `protected`, `internal`, `static`, `extern`, `new`, `virtual`, `abstract`, `sealed`, `override`, `readonly`, `unsafe`, `volatile`, `async` + +## Build and Test Requirements + +### Building the Project +- **Always build in RELEASE mode** for validation: `dotnet build -c Release` +- All warnings are treated as errors in Release configuration +- Projects must be signed with strong name (`key.snk`) +- Enable all analyzers: `AnalysisMode` is set to `AllEnabledByDefault` + +### Testing Requirements +- **All tests must pass** before submitting changes +- Run tests in Release mode: `dotnet test -c Release --no-restore` +- Tests must be provided for every bug fix and feature +- Test projects can use relaxed analyzer rules (see `tests/**/.editorconfig`) +- Tests should be specific to the changes being made +- Support for multiple test frameworks: xUnit, NUnit, MSTest, TUnit + +### Verification Steps +Before submitting any changes, ensure: +1. `dotnet restore` completes successfully +2. `dotnet build -c Release` completes without warnings or errors +3. `dotnet test -c Release` completes with all tests passing +4. Code adheres to .editorconfig rules (enforced during build) + +## Code Quality and Analysis + +### Analyzers +- **Microsoft Code Analysis**: Enabled with all rules +- **StyleCop**: Enforced via .editorconfig +- **Meziantou Analyzer**: Specific rules enabled +- **SonarAnalyzer**: Enabled with specific suppressions +- Code style violations are enforced in build (`EnforceCodeStyleInBuild: true`) + +### Code Analysis Rules +- Default severity for .NET Code Style: **error** +- `TreatWarningsAsErrors`: **true** in Release mode +- Unused parameters must be removed (`IDE0060`) +- Unnecessary suppressions must be removed (`IDE0079`) + +## Development Practices + +### Version Control +- Follow **trunk-based development** - base changes on `main` branch +- Use **Conventional Commits** style for commit messages + - `feat:` for new features + - `fix:` for bug fixes + - `docs:` for documentation changes + - `test:` for test additions/changes + - `refactor:` for code refactoring + - `chore:` for maintenance tasks + +### Pull Request Guidelines +- Ensure repository can build successfully +- All tests must pass +- Follow existing coding conventions (aligned with ASP.NET Core team guidelines) +- PRs should be focused and specific to the issue being addressed +- Add/update tests to cover your changes + +## Project Structure + +### Main Projects +- `bunit` - Main package with all functionality +- `bunit.core` - Core testing functionality +- `bunit.web` - Web-specific components testing +- `bunit.web.query` - Testing-library.com query API implementation +- `bunit.generators` - Source generators +- `bunit.template` - Project templates (xUnit, NUnit, MSTest) + +### Test Projects +- `bunit.tests` - Main test suite +- `bunit.web.query.tests` - Query API tests +- `bunit.generators.tests` - Source generator tests +- `bunit.testassets` - Shared test assets + +## Additional Guidelines + +### Documentation +- Update relevant documentation when making changes +- Keep XML documentation comments current +- Documentation must be clear and concise +- Code examples should be tested and working + +### Dependencies +- Central Package Management is enabled (`Directory.Packages.props`) +- Follow semantic versioning for package references +- Minimize external dependencies + +### Implicit Usings +The following namespaces are automatically imported (except in template and generators projects): +- `Microsoft.AspNetCore.Components` +- `Microsoft.AspNetCore.Components.RenderTree` +- `Microsoft.AspNetCore.Components.Rendering` +- `Microsoft.Extensions.DependencyInjection` +- `System.Runtime.Serialization` +- `System.Diagnostics.CodeAnalysis` + +### Performance Considerations +- Tests should run in milliseconds (not seconds) +- Avoid blocking calls in async methods +- Use `ConfigureAwait(false)` where appropriate (except in test code) + +### Code of Conduct +- Follow the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct) +- Be respectful and collaborative in all interactions + +## Quick Checklist for Copilot + +When making changes, always: +- ✅ Use tabs for indentation +- ✅ Enable nullable reference types +- ✅ Use file-scoped namespaces +- ✅ Build in Release mode (`-c Release`) +- ✅ Run all tests in Release mode +- ✅ Ensure no warnings (warnings = errors in Release) +- ✅ Follow naming conventions (PascalCase for public, camelCase for private) +- ✅ Add XML documentation for public APIs +- ✅ Write tests for all changes +- ✅ Use conventional commit message format +- ✅ Verify all analyzer rules pass +- ✅ Check that code compiles against all target frameworks (.NET 8, 9, 10) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..f4e641aaf --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..4cf2dbecc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +## Pull request description + + +### PR meta checklist +- [ ] Pull request is targeted at `main` branch for code + or targeted at `stable` branch for documentation that is live on bunit.dev. +- [ ] Pull request is linked to all related issues, if any. +- [ ] I have read the _CONTRIBUTING.md_ document. + +### Code PR specific checklist +- [ ] My code follows the code style of this project and AspNetCore coding guidelines. +- [ ] My change requires a change to the documentation. + - [ ] I have updated the documentation accordingly. +- [ ] I have updated the appropriate sub section in the _CHANGELOG.md_. +- [ ] I have added, updated or removed tests to according to my changes. + - [ ] All tests passed. diff --git a/.github/workflows/CI-CD-Docs.yml b/.github/workflows/CI-CD-Docs.yml deleted file mode 100644 index 4a3fe1561..000000000 --- a/.github/workflows/CI-CD-Docs.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build and Deploy Docs - -on: - push: - branches: - - master - paths-ignore: - - '!docs/**' - - '.github/**' - -env: - VERSION: '1.0.0-beta-6' - BRANCH: 'master' - COMMIT: '' - DOCFX_SOURCE_BRANCH_NAME: 'master' - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - persist-credentials: false - - name: Update tokens in project files - uses: cschleiden/replace-tokens@v1 - with: - files: '["docs/*.md", "docs/**/*.md", "*.csproj", "**/*.csproj"]' - - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.101' - - name: Building library and docs - run: | - dotnet build src/bunit.csproj - dotnet build docs/docs.csproj - - name: Deploy - uses: JamesIves/github-pages-deploy-action@releases/v3 - with: - ACCESS_TOKEN: ${{ secrets.GH_DOCS }} - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: docs/_site # The folder the action should deploy. - CLEAN: true diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index d446ea9b6..000000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: CI - -on: - push: - paths-ignore: - - 'docs/**' - - '.github/**' - -env: - VERSION: 1337.0.0 - BRANCH: '' - COMMIT: '' - -jobs: - build: - name: Build and verify library - runs-on: ubuntu-latest - steps: - - name: Setting environment variables - run: | - echo "::set-env name=BRANCH::$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//_/g')" - echo "::set-env name=COMMIT::$GITHUB_SHA" - - run: echo Version $VERSION - Branch $BRANCH - Commit $COMMIT - - uses: actions/checkout@v1 - - name: Update tokens in project files - uses: cschleiden/replace-tokens@v1 - with: - files: '["*.csproj", "**/*.csproj"]' - - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.101' - - name: Building and verifying library - run: | - dotnet build -c Release - dotnet test -c Release /nowarn:CS1591 - dotnet build sample -c Release - dotnet test sample -c Release - - name: Creating library package - run: dotnet pack src/ -c Release -o ${GITHUB_WORKSPACE}/lib -p:version=$VERSION /nowarn:CS1591 - - name: Buidling template package - run: dotnet pack template/ -c Release -o ${GITHUB_WORKSPACE}/template -p:version=$VERSION -p:PackageVersion=$VERSION - - name: Verifying template - run: | - dotnet new --install ${GITHUB_WORKSPACE}/template/bunit.template.$VERSION.nupkg - dotnet new bunit -o ${GITHUB_WORKSPACE}/Test - dotnet restore ${GITHUB_WORKSPACE}/Test/Test.csproj --source ${GITHUB_WORKSPACE}/lib - dotnet test ${GITHUB_WORKSPACE}/Test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..415d1cd40 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,296 @@ +name: ci + +on: + push: + branches: + - main + paths: + - 'src/**' + - 'tests/**' + - 'docs/**' + + pull_request: + types: + - opened + - synchronize + - reopened + + workflow_dispatch: + +concurrency: + group: ci-${{ github.workflow }}-${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v2') && github.run_id || github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + VSTEST_CONNECTION_TIMEOUT: 180 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_NOLOGO: true + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + NUGET_DIRECTORY: ${{ github.workspace }}/nuget + NUGET_PACKAGES_ARTIFACT: nuget-packages + DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 + TERM: xterm + +jobs: + create-nuget: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # Get all history to allow automatic versioning using MinVer + + # Install the .NET SDK indicated in the global.json file + - name: ⚙️ Setup dotnet versions + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + 11.0.x + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + id: nbgv + with: + setAllVars: true + + - name: 🛠️ Get Changelog Entry + id: changelog_reader + uses: mindsers/changelog-reader-action@v2 + with: + version: Unreleased + path: ./CHANGELOG.md + + - name: 🛠️ Update tokens in project files + uses: cschleiden/replace-tokens@v1 + with: + files: '["docs/site/*.md", "docs/**/*.md", "docs/**/*.tmpl.partial", "*.csproj", "**/*.csproj", "src/Directory.Build.props"]' + env: + RELEASE_VERSION: ${{ steps.nbgv.outputs.SimpleVersion }}${{ steps.nbgv.outputs.PrereleaseVersion }} + RELEASE_NOTES: ${{ steps.changelog_reader.outputs.changes }} + + # Create the NuGet package in the folder from the environment variable NuGetDirectory + - run: | + dotnet pack src/bunit/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.template/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.web.query/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.generators/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true + + # Publish the NuGet package as an artifact, so they can be used in the following jobs + - uses: actions/upload-artifact@v7 + with: + name: ${{ env.NUGET_PACKAGES_ARTIFACT }} + if-no-files-found: error + compression-level: 0 # nuget files already compressed + retention-days: 7 + path: ${{ env.NUGET_DIRECTORY }}/*.nupkg + + validate-nuget: + runs-on: ubuntu-latest + needs: [ create-nuget ] + steps: + - name: Setup .NET + uses: actions/setup-dotnet@v6 + + - uses: actions/download-artifact@v8 + with: + name: ${{ env.NUGET_PACKAGES_ARTIFACT }} + path: ${{ env.NUGET_DIRECTORY }} + + - name: Install nuget validator + run: dotnet tool update Meziantou.Framework.NuGetPackageValidation.Tool --global + + # Validate metadata and content of the NuGet package + # https://www.nuget.org/packages/Meziantou.Framework.NuGetPackageValidation.Tool#readme-body-tab + # If some rules are not applicable, you can disable them + # using the --excluded-rules or --excluded-rule-ids option + - name: Validate package + shell: pwsh + run: meziantou.validate-nuget-package (Get-ChildItem "${{ env.NUGET_DIRECTORY }}/*.nupkg") --excluded-rules IconMustBeSet + + run-test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + + steps: + - name: 🛒 Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: ⚙️ Setup dotnet versions + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + 11.0.x + + - name: ⚙️ Restore packages and tools + run: | + dotnet restore + dotnet tool restore + + - name: 🧪 Run unit tests + run: dotnet test -c release --no-restore + + - name: 📛 Upload hang- and crash-dumps on test failure + if: success() || failure() + uses: actions/upload-artifact@v7 + with: + if-no-files-found: ignore + name: test-dumps + path: | + **/*.dmp + **/*.dmp + + validate-template: + runs-on: ubuntu-latest + needs: [ create-nuget ] + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # Get all history to allow automatic versioning using MinVer + + - name: ⚙️ Setup dotnet + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 10.0.x + + - uses: actions/download-artifact@v8 + with: + name: ${{ env.NUGET_PACKAGES_ARTIFACT }} + path: ${{ env.NUGET_DIRECTORY }} + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + with: + setAllVars: true + + - name: ✳ Install bUnit template + run: | + dotnet new install bunit.template::${NBGV_SimpleVersion}${NBGV_PrereleaseVersion} --nuget-source ${{ env.NUGET_DIRECTORY }} + + - name: ✔ Verify xUnit template + run: | + dotnet new bunit --no-restore -o ${{ github.workspace }}/TemplateTestXunit + echo '' >> ${{ github.workspace }}/TemplateTestXunit/Directory.Build.props + echo 'false' >> ${{ github.workspace }}/TemplateTestXunit/Directory.Packages.props + dotnet restore ${{ github.workspace }}/TemplateTestXunit --source https://api.nuget.org/v3/index.json --source ${{ env.NUGET_DIRECTORY }} + dotnet test ${{ github.workspace }}/TemplateTestXunit + + - name: ✔ Verify xUnit.v3 template + run: | + dotnet new bunit --framework xunitv3 --no-restore -o ${{ github.workspace }}/TemplateTestXunitv3 + echo '' >> ${{ github.workspace }}/TemplateTestXunitv3/Directory.Build.props + echo 'false' >> ${{ github.workspace }}/TemplateTestXunitv3/Directory.Packages.props + dotnet restore ${{ github.workspace }}/TemplateTestXunitv3 --source https://api.nuget.org/v3/index.json --source ${{ env.NUGET_DIRECTORY }} + dotnet test ${{ github.workspace }}/TemplateTestXunitv3 + + - name: ✔ Verify NUnit template + run: | + dotnet new bunit --framework nunit --no-restore -o ${{ github.workspace }}/TemplateTestNunit + echo '' >> ${{ github.workspace }}/TemplateTestNunit/Directory.Build.props + echo 'false' >> ${{ github.workspace }}/TemplateTestNunit/Directory.Packages.props + dotnet restore ${{ github.workspace }}/TemplateTestNunit --source https://api.nuget.org/v3/index.json --source ${{ env.NUGET_DIRECTORY }} + dotnet test ${{ github.workspace }}/TemplateTestNunit + + - name: ✔ Verify MSTest template + run: | + dotnet new bunit --framework mstest --no-restore -o ${{ github.workspace }}/TemplateTestMstest + echo '' >> ${{ github.workspace }}/TemplateTestMstest/Directory.Build.props + echo 'false' >> ${{ github.workspace }}/TemplateTestMstest/Directory.Packages.props + dotnet restore ${{ github.workspace }}/TemplateTestMstest --source https://api.nuget.org/v3/index.json --source ${{ env.NUGET_DIRECTORY }} + dotnet test ${{ github.workspace }}/TemplateTestMstest + + validate-docs: + runs-on: ubuntu-latest + steps: + - name: 🛒 Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: ⚙️ Setup dotnet versions + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + 11.0.x + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + id: nbgv + with: + setAllVars: true + + - name: 🛠️ Get Changelog Entry + id: changelog_reader + uses: mindsers/changelog-reader-action@v2 + with: + version: Unreleased + path: ./CHANGELOG.md + + - name: 🍥 Replace tokens in files + uses: cschleiden/replace-tokens@v1 + with: + files: '["docs/site/*.md", "docs/**/*.md", "docs/**/*.tmpl.partial", "*.csproj", "**/*.csproj", "src/Directory.Build.props", "docs/site/docfx.json"]' + env: + RELEASE_VERSION: ${{ steps.nbgv.outputs.SimpleVersion }}${{ steps.nbgv.outputs.PrereleaseVersion }} + RELEASE_NOTES: ${{ steps.changelog_reader.outputs.changes }} + + - name: 📄 Build bUnit + run: dotnet build -c release + + - name: 🧪 Run sample unit tests + run: dotnet test docs/samples/samples.sln -p:VSTestUseMSBuildOutput=false -f net8.0 + + - name: 📄 Build docs + working-directory: ./docs/site + run: | + dotnet tool restore + dotnet docfx metadata --logLevel error + dotnet docfx build --logLevel warning --warningsAsErrors + + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + if: github.event_name == 'pull_request' && github.repository_owner == 'bunit-dev' + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v7 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v5 + + release-preview: + if: github.event_name == 'workflow_dispatch' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v2') + runs-on: ubuntu-latest + needs: [ validate-nuget, run-test, validate-template, validate-docs ] + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v8 + with: + name: ${{ env.NUGET_PACKAGES_ARTIFACT }} + path: ${{ env.NUGET_DIRECTORY }} + + - name: Setup .NET Core + uses: actions/setup-dotnet@v6 + + - name: 🛠️ NuGet login + uses: NuGet/login@v1 + id: nuget-login + with: + user: ${{ secrets.NUGET_USER }} + + - name: 🛠️ Upload library to NuGet.org repository + run: dotnet nuget push ${{ env.NUGET_DIRECTORY }}/*.nupkg -k ${{ steps.nuget-login.outputs.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 000000000..81075fccc --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,134 @@ +name: "docs-deploy" +concurrency: "docs-deploy" + +on: + push: + branches: + - stable + paths: + - 'docs/**' + + workflow_run: + branches: [ main ] + workflows: [ 'release' ] + types: [ completed ] + + workflow_dispatch: + +env: + VSTEST_CONNECTION_TIMEOUT: 180 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_NOLOGO: true + DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 + TERM: xterm + +jobs: + deploy: + name: 📃 Deploy new version bunit.dev + if: github.ref == 'refs/heads/stable' || ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + steps: + + - name: 🛒 Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + token: ${{ secrets.BUNIT_BOT_TOKEN }} + + - name: ⚙️ Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v7 + with: + gpg_private_key: ${{ secrets.BUNIT_BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BUNIT_BOT_GPG_KEY_PASSPHRASE }} + + - name: ⚙️ Setup CI GIT + run: | + git config user.name "${{ steps.import_gpg.outputs.name }}" + git config user.email ${{ steps.import_gpg.outputs.email }} + git config --global user.signingkey ${{ steps.import_gpg.outputs.keyid }} + git config --global commit.gpgsign true + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + id: nbgv + with: + setAllVars: true + + - name: 🛠️ Get Changelog Entry + id: changelog_reader + uses: mindsers/changelog-reader-action@v2 + with: + path: ./CHANGELOG.md + + - name: 🛠️ Update tokens in files + uses: cschleiden/replace-tokens@v1 + with: + files: '["docs/site/*.md", "docs/**/*.md", "docs/**/docfx.json", "*.csproj", "**/*.csproj", "src/Directory.Build.props"]' + env: + RELEASE_VERSION: ${{ steps.changelog_reader.outputs.version }} + RELEASE_NOTES: ${{ steps.changelog_reader.outputs.changes }} + + - name: ⚙️ Setup dotnet versions + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + 11.0.x + + - name: 🎨 Setup color + run: | + echo "DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION=1" >> $GITHUB_ENV + echo "TERM=xterm" >> $GITHUB_ENV + + - name: 🛠️ Building bUnit + run: dotnet build /p:PublicRelease=true -c release + + - name: 🛠️ Verify docs samples + run: dotnet test docs/samples/samples.sln + + - name: 📄 Building docs + working-directory: ./docs/site + run: | + dotnet tool install --global docfx --version 2.74.1 + docfx metadata --logLevel error + docfx build + + - name: 🛠️ Deploy to GitHub Pages + if: success() + uses: crazy-max/ghaction-github-pages@v5 + with: + build_dir: docs/site/_site + fqdn: bunit.dev + repo: bUnit-dev/bUnit-dev.github.io + target_branch: main + keep_history: false + jekyll: false + committer: "bUnit bot " + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_PAT: ${{ secrets.BUNIT_BOT_TOKEN }} + + - name: ⏩ Merge stable with main, push origin + id: mergeMainline + continue-on-error: true + run: | + git checkout main + git merge -S stable + git push origin main + + - name: ⏭ Create pull request from stable to main when direct merge fails + if: steps.mergeMainline.outcome == 'failure' + uses: thomaseizinger/create-pull-request@1.4.0 + with: + github_token: ${{ secrets.BUNIT_BOT_TOKEN }} + head: stable + base: main + title: Update main with documentation in stable + reviewers: ${{ github.actor }} # By default, we request a review from the person who triggered the workflow. + body: | + Hi @${{ github.actor }} + + This PR was created because the [docs-deploy](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) failed to automatically merge stable into main. diff --git a/.github/workflows/nuget-pack-push.yml b/.github/workflows/nuget-pack-push.yml deleted file mode 100644 index 1ad5e15e8..000000000 --- a/.github/workflows/nuget-pack-push.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: NUGET-PACK-PUSH - -on: - release: - types: - - published - - edited - -env: - VERSION: 0.0.0 - BRANCH: "master" - COMMIT: "" - -jobs: - build: - name: Build and verify library - runs-on: ubuntu-latest - steps: - - name: Setting environment variables - run: | - #echo "::set-env name=BRANCH::$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//_/g')" - echo "::set-env name=COMMIT::$GITHUB_SHA" - echo "::set-env name=VERSION::${GITHUB_REF/refs\/tags\/v/}" - - run: echo Version $VERSION - Branch $BRANCH - Commit $COMMIT - - uses: actions/checkout@v1 - - name: Update tokens in project files - uses: cschleiden/replace-tokens@v1 - with: - files: '["*.csproj", "**/*.csproj"]' - - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.100' - - name: Building and verifying library - run: | - dotnet build -c Release /nowarn:CS1591 - dotnet test -c Release /nowarn:CS1591 - - name: Creating library package - run: dotnet pack src/ -c Release -o ${GITHUB_WORKSPACE}/lib -p:version=$VERSION /nowarn:CS1591 - - name: Buidling template package - run: dotnet pack template/ -c Release -o ${GITHUB_WORKSPACE}/template -p:version=$VERSION -p:PackageVersion=$VERSION - - name: Verifying template - run: | - dotnet new --install ${GITHUB_WORKSPACE}/template/bunit.template.$VERSION.nupkg - dotnet new bunit -o ${GITHUB_WORKSPACE}/Test - dotnet restore ${GITHUB_WORKSPACE}/Test/Test.csproj --source ${GITHUB_WORKSPACE}/lib - dotnet test ${GITHUB_WORKSPACE}/Test - - name: Push packages to NuGet.org - run: | - dotnet nuget push ${GITHUB_WORKSPACE}/lib/*.nupkg -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate - dotnet nuget push ${GITHUB_WORKSPACE}/template/*.nupkg -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 000000000..490fa4934 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,109 @@ +name: prepare-release +concurrency: 'prepare-release' + +on: + workflow_dispatch: + inputs: + versionIncrement: + description: 'The version increment. Allowed values are "major", "minor" and "build".' + type: choice + required: true + options: + - major + - minor + - build + default: 'minor' + +jobs: + prepare-release: + name: 🚚 Prepare new release + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && contains(fromJson('["major","minor","build"]'), github.event.inputs.versionIncrement) + steps: + - name: 🛒 Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + token: ${{ secrets.BUNIT_BOT_TOKEN }} + + - name: 🛠️ Get Changelog Entry + id: changelog_reader + uses: mindsers/changelog-reader-action@v2 + with: + version: Unreleased + path: ./CHANGELOG.md + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + id: nbgv + with: + setAllVars: true + + - name: ☑ Check that release contains changes + if: steps.changelog_reader.outputs.changes == '' + run: | + echo "::error file=CHANGELOG.md::The unreleased section in the changelog is empty. Nothing to release." + exit 1 + + - name: ⚙️ Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v7 + with: + gpg_private_key: ${{ secrets.BUNIT_BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BUNIT_BOT_GPG_KEY_PASSPHRASE }} + + - name: ⚙️ Setup CI GIT + run: | + git config user.name "${{ steps.import_gpg.outputs.name }}" + git config user.email ${{ steps.import_gpg.outputs.email }} + git config --global user.signingkey ${{ steps.import_gpg.outputs.keyid }} + git config --global commit.gpgsign true + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + with: + setAllVars: true + + - name: 🛠️ Increment version.json on main + create release branch + id: versions + run: | + nbgv prepare-release --versionIncrement ${{ github.event.inputs.versionIncrement }} + echo "MAIN_VERSION_COMMIT_MESSAGE=$(git log --format=%B -n 1 --skip 1)" >> $GITHUB_OUTPUT + git checkout release/v$NBGV_MajorMinorVersion + echo "RELEASE_VERSION_COMMIT_MESSAGE=$(git log --format=%B -n 1)" >> $GITHUB_OUTPUT + + # Workaround since nbgv prepare-release does not sign commits. + # This undo's the commits, keeps the version changes, and commits again with signing + # with the saved commit messages from previous step. + # See: https://github.com/dotnet/Nerdbank.GitVersioning/issues/594 + - name: 🛠️ Sign commits on main and release branch + run: | + git checkout release/v$NBGV_MajorMinorVersion + git reset --soft HEAD~1 + git commit -S -m "${{ steps.versions.outputs.RELEASE_VERSION_COMMIT_MESSAGE }}" + + git checkout main + git reset --hard HEAD~1 + git reset --soft HEAD~1 + git commit -S -m "${{ steps.versions.outputs.MAIN_VERSION_COMMIT_MESSAGE }}" + git merge -S -X ours release/v$NBGV_MajorMinorVersion + + - name: ⏩ Push version.json updates to main + run: git push origin main + + - name: ⏩ Push release branch to origin + run: git push origin release/v$NBGV_MajorMinorVersion + + - name: ⏭ Create pull request for release branch + uses: thomaseizinger/create-pull-request@1.4.0 + with: + github_token: ${{ secrets.BUNIT_BOT_TOKEN }} + head: release/v${{ steps.nbgv.outputs.MajorMinorVersion }} + base: stable + title: Release of new ${{ github.event.inputs.versionIncrement }} version v${{ steps.nbgv.outputs.MajorMinorVersion }} + body: | + This PR was created in response to a manual trigger of the [prepare-release workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}). + + Merging this PR will create a GitHub Release and push new packages to NuGet. + + **NOTE:** Only small fixes should be added to this PR at this point. If you need to make minor or major changes, close the PR and make those changes to ${{ github.ref }} instead, and run the prepare-release workflow again once you are done. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c6284e036 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,154 @@ +name: release +concurrency: 'release' + +on: + pull_request: + branches: + - stable + types: + - closed + paths-ignore: + - 'docs/**' + + workflow_dispatch: + +env: + VSTEST_CONNECTION_TIMEOUT: 180 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_NOLOGO: true + DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 + TERM: xterm + +jobs: + release: + name: 🎁 Publish new release + # Only run on manual dispatch or on merged 'release-' pull requests + if: github.ref == 'refs/heads/stable' || + github.event.pull_request.merged == true && contains(github.head_ref, 'release/v') == true + runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + issues: write + pull-requests: write + steps: + + - name: 🛒 Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + token: ${{ secrets.BUNIT_BOT_TOKEN }} + + - name: ⚙️ Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v7 + with: + gpg_private_key: ${{ secrets.BUNIT_BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BUNIT_BOT_GPG_KEY_PASSPHRASE }} + + - name: ⚙️ Setup CI GIT + run: | + git config user.name "${{ steps.import_gpg.outputs.name }}" + git config user.email ${{ steps.import_gpg.outputs.email }} + git config --global user.signingkey ${{ steps.import_gpg.outputs.keyid }} + git config --global commit.gpgsign true + + - name: ⚙️ Setup GIT versioning + uses: dotnet/nbgv@v0.5.2 + id: nbgv + with: + setAllVars: true + + - name: ⚙️ Setup dotnet versions + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + 11.0.x + + - name: 🛠️ Update changelog + uses: thomaseizinger/keep-a-changelog-new-release@3.1.0 + with: + version: ${{ steps.nbgv.outputs.SemVer2 }} + + - name: 🛠️ Update changelog compare URLs + shell: bash + run: | + sed -i -E "s/compare\/([0-9]+\.[0-9]+\.[0-9]+)\.\.\.([0-9]+\.[0-9]+\.[0-9]+)/compare\/v\1...v\2/" CHANGELOG.md + sed -i -E "s/compare\/([0-9]+\.[0-9]+\.[0-9]+)\.\.\.HEAD/compare\/v\1...HEAD/" CHANGELOG.md + + - name: 🛠️ Commit CHANGELOG.md to stable branch + run: | + git add CHANGELOG.md + git commit -S -m "Updated CHANGELOG.md for ${{ steps.nbgv.outputs.SimpleVersion }} release" + echo "RELEASE_COMMIT_HASH=$(git rev-parse stable)" >> $GITHUB_ENV + + - name: 🛠️ Get Changelog Entry + id: changelog_reader + uses: mindsers/changelog-reader-action@v2 + with: + version: ${{ steps.nbgv.outputs.SemVer2 }} + path: ./CHANGELOG.md + + - name: 🛠️ Update tokens in project files + uses: cschleiden/replace-tokens@v1 + with: + files: '["docs/site/*.md", "docs/**/*.md", "docs/**/*.tmpl.partial", "*.csproj", "**/*.csproj", "src/Directory.Build.props"]' + env: + RELEASE_VERSION: ${{ steps.nbgv.outputs.NuGetPackageVersion }} + RELEASE_NOTES: ${{ steps.changelog_reader.outputs.changes }} + + - name: 🛠️ Packing library in release mode + run: | + dotnet pack src/bunit/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.template/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.web.query/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.generators/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true + + - name: 🛠️ NuGet login + uses: NuGet/login@v1 + id: nuget-login + with: + user: ${{ secrets.NUGET_USER }} + + - name: 🛠️ Upload library to NuGet.org repository + run: | + dotnet nuget push ${GITHUB_WORKSPACE}/packages/*.nupkg -k ${{ steps.nuget-login.outputs.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols + + - name: ⏩ Push stable branch + run: git push origin stable + + - name: 🛠️ Create GitHub release + uses: thomaseizinger/create-release@2.0.0 + with: + tag_name: v${{ steps.nbgv.outputs.SemVer2 }} + target_commitish: ${{ env.RELEASE_COMMIT_HASH }} + name: ${{ steps.nbgv.outputs.SemVer2 }} + body: ${{ steps.changelog_reader.outputs.changes }} + draft: false + prerelease: ${{ steps.nbgv.outputs.PublicRelease == 'False' }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: ⏩ Merge stable with main, push to origin + id: mergeMainline + continue-on-error: true + run: | + git checkout main + git merge -S stable + git push origin main + + - name: ⏭ Create pull request from stable to main when direct merge fails + if: steps.mergeMainline.outcome == 'failure' + uses: thomaseizinger/create-pull-request@1.4.0 + with: + github_token: ${{ secrets.BUNIT_BOT_TOKEN }} + head: stable + base: main + title: Update main with changes in stable after v${{ steps.nbgv.outputs.SemVer2 }} release + reviewers: ${{ github.actor }} # By default, we request a review from the person who triggered the workflow. + body: | + Hi @${{ github.actor }} + + This PR was created because the [release workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) failed to automatically merge stable into main. diff --git a/.gitignore b/.gitignore index 9b02d72a7..77cd6d772 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.suo @@ -26,7 +26,6 @@ bld/ # Visual Studio 2015/2017 cache/options directory .vs/ -.vscode/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ @@ -331,3 +330,14 @@ ASALocalRun/ .mfractor/ *.playlist bunit.docs/log.txt + +.store +*coverage*.info +.sonarqube +tests/*/coverage + +bunit.v3.ncrunchsolution.user +watch.csproj + +# MacOS +.DS_Store \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..458ec69a3 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,22 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Serve Docs (Without Build)", + "type": "shell", + "command": "dotnet docfx metadata docs/site/docfx.json && dotnet docfx docs/site/docfx.json --serve" + }, + { + "label": "Serve Docs (With Build for API Documentation)", + "type": "shell", + "command": "dotnet build -c Release && dotnet docfx metadata docs/site/docfx.json && docfx docs/site/docfx.json --serve" + }, + { + "label": "Run all tests (Release Mode)", + "type": "shell", + "command": "dotnet test -c Release" + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 57adb1487..676334358 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,31 +1,1318 @@ # Changelog + All notable changes to **bUnit** will be documented in this file. The project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] + + +## [Unreleased] + +## [2.9.0] - 2026-08-03 + +### Changed + +- Update to stable package of `AngleSharp.Css` + +## [2.8.6] - 2026-07-27 + +### Added + +- New overloads to WaitForHelpers to have async assertions and predicates. Reported by [@radmorecameron](https://github.com/radmorecameron) in #1833. Fixed by [@linkdotnet](https://github.com/linkdotnet). +- `AddAsset` to `BunitContext` to seed the `ResourceAssetCollection` exposed via `ComponentBase.Assets`. Reported by [LasseHerget](https://github.com/LasseHerget) in #1846. Implemented by [@linkdotnet](https://github.com/linkdotnet). + +## [2.7.2] - 2026-03-31 + +### Fixed + +- Implemented `InvokeConstructorAsync` on `BunitJSRuntime` and `BunitJSObjectReference` for .NET 10+, which previously threw `NotImplementedException`. Reported by [@Floopy-Doo](https://github.com/Floopy-Doo) in #1818. Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [2.6.2] - 2026-02-27 + +### Added + +- net11.0 support + +## [2.5.3] - 2026-01-08 + +### Added + +- `Render(RenderFragment)` is preferred via the `OverloadResolutionAttribute`. Reported by [@ScarletKuro](https://github.com/ScarletKuro) in #1800. Fixed by [@linkdotnet](https://github.com/linkdotnet). +- `FindByTestId` to `bunit.web.query` to gather elements by a given test id. By [@jimSampica](https://github.com/jimSampica) + +## [2.4.2] - 2025-12-21 + +### Fixed + +- Use proper return typed for `InputAsync` and `ChangeAsync` methods. + +## [2.3.4] - 2025-12-18 + +### Added + +- Added generic overloads `Find{TComponent, TElement}` and `FindAll{TComponent, TElement}` to query for specific element types (e.g., `IHtmlInputElement`). By [@linkdotnet](https://github.com/linkdotnet). +- Added generic overloads `WaitForElement{TComponent, TElement}` and `WaitForElements{TComponent, TElement}` to wait for specific element types. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixed + +- Adding convenient overloads for `InputAsync` and `ChangeAsync` to have feature parity with the sync version. Reported by [@ScarletKuro](https://github.com/ScarletKuro). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [2.2.2] - 2025-12-08 + +### Added + +- Added `FindByAllByLabel` to `bunit.web.query` package. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixed + +- Updated `AngleSharp.Diffing` to fix a bug related to unknown HTML elements. Reported by [@md-at-slashwhy](https://github.com/md-at-slashwhy). + +## [2.1.1] - 2025-11-21 + +### Changed + +- Registering `AuthenticationState` in the services container rather than as part of the RenderTree. Fixes [#1774](https://github.com/bUnit-dev/bUnit/issues/1774) reported by [@aayjaychan](https://github.com/aayjaychan). + +## [2.0.66] - 2025-11-11 + +This major release focuses on platform updates and API simplifications. + +For a migration guide, see [Upgrading bUnit](https://bunit.dev/docs/migrations/index.html). + +### Changed + +- Target framework support updated: added support for .NET 10 (`net10.0`) and dropped all versions prior to .NET 8 (`net8.0`). +- Cleanup of the API with simplifications of many API calls and methods. This also includes renaming of some objects to better reflect their purpose. + +### Added + +- Support for form submission from submit buttons and inputs that are outside the form element but associated via the HTML5 `form` attribute. Reported and fixed in [#1766](https://github.com/bUnit-dev/bUnit/issues/1766). +- Improved renderer logic that catches more edge cases. +- Improved developer experience in relation to JSInterop. + +## [1.40.0] - 2025-06-14 + +### Fixed + +- Aligned Microsoft packages to their TFM version + +## [1.39.5] - 2025-04-04 + +### Fixed + +- Do not set the `Uri` or `BaseUri` property on the `FakeNavigationManager` if navigation is prevented by a handler on `net7.0` or greater. Reported and fixed by [@ayyron-dev](https://github.com/ayyron-dev) in [#1647](https://github.com/bUnit-dev/bUnit/issues/1647) +- Use default renderer properties for AngleSharp. Reported by [@jtleaming](https://github.com/jtleaming) in [#1692]. +- `FindComponents` throws an exception, when a base and derived class was searched for. Reported by [@BlueDragon709](https://github.com/BlueDragon709) in [#1691]. + +## [1.38.5] - 2025-01-12 + +### Added + +- Added support for xunit v3 in the bunit.template. By [@linkdotnet](https://github.com/linkdotnet). + +## [1.37.7] - 2024-12-13 + +### Added + +- Added support for `RendererInfo` and `AssignedRenderMode` (`.net9.0`). + +## [1.36.0] - 2024-11-12 + +### Added + +- Bumped .NET 9 version dependencies to stable packages. + +## [1.35.3] - 2024-11-12 + +### Added + +- Extension packages (`bunit.generators` and `bunit.web.query`) are flagged as stable. + +## [1.34.0] - 2024-11-01 + +### Fixed + +- Fixed other packages that have a CVE like `Microsoft.Extensions.Caching.Memory`. Reported by [@polajenko](https://github.com/polajenko). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.33.3] - 2024-10-11 + +### Added + +- `bunit.generators` respect parameters from the base class. +- Supports components using constructor injection in `net9.0`. + +### Fixed + +- Use latest `System.Text.Json` due to CVE in `8.0.4`. + +## [1.32.7] - 2024-10-04 + +### Fixed + +- Ensure that a check in `WaitForAssertion` and related methods is always performed at once before the wait timer is started. If not, the timeout could occure before a wait-for check-condition had been attempted, causing tests to fail without reason. Fixed by [@egil](https://github.com/egil). + +## [1.31.3] - 2024-08-16 + +### Fixed + +- `UploadFile` should only throw an exception when the file size exceeds the maximum allowed size. Reported by [@candritzky](https://github.com/candritzky). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.30.3] - 2024-07-21 + +### Fixed + +- `UploadFile` doesn't throw an exception when the file size exceeds the maximum allowed size. Reported by [@MorneZaayman](https://github.com/MorneZaayman) in [#1503](https://github.com/bUnit-dev/bUnit/issues/1503). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.29.5] - 2024-07-05 + +### Fixed + +- CI build changes to force running verification on x64 based AMD CPUs. + +### Added + +- New overloads for `ComponentParameterCollectionBuilder.Add` that allow passing arguments for asynchronous callback parameters. Reported by [springy76](https://github.com/springy76). By [@Qwertyluk](https://github.com/Qwertyluk). + +## [1.28.9] - 2024-04-19 + +### Fixed + +- bUnit's built-in HTML parser did not correctly parse full HTML documents that included a <!DOCTYPE html> as the first element. Fixed by [@egil](https://github.com/egil). +- `@formname` directive led to an `InvalidOperationException` when used on a form element. Reported by [@suzu2469](https://github.com/suzu2469) in [#1438](https://github.com/bUnit-dev/bUnit/issues/1438). + Fixed by [@egil](https://github.com/egil)/[@linkdotnet](https://github.com/linkdotnet). + +### Changed + +- `NavigationManager` is again registered as a singleton instead of scoped. + +## [1.27.17] - 2024-03-02 + +### Added + +- Support for `IKeyedServiceProvider` in net8.0. Reported by [@ViRuSTriNiTy](https://github.com/ViRuSTriNiTy). By [@linkdotnet](https://github.com/linkdotnet). +- Support for `net9.0`. NOTE, there is no commitment as of now to support net9.0 in bUnit v1. However. Support for net9.0 may move to a future v2 release of bUnit and be deprecated in v1. However, allowing bUnit to build and work with net9.0 previews allows our users to keep testing! + +### Fixed + +- Support for `SupplyFromQueryParameter` in net8.0. Reported by [@aayjaychan](https://github.com/aayjaychan). Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). + +## [1.26.64] - 2023-12-20 + +### Changed + +- Upgraded AngleSharp to 1.0.7. Info: The usage of `AngleSharpWrappers` is not needed anymore. Any usage of `Unwrap` should not be needed anymore. + +### Fixed + +- When the `TestContext` was disposed, the Blazor Renderer itself didn't dispose components under test. By [@linkdotnet](https://github.com/linkdotnet). +- When navigating, the `HistoryEntryState` on `NavigationManager` will be populated. By [@linkdotnet](https://github.com/linkdotnet). + +### Added + +- New overloads for `IRenderedFragmentBase.InvokeAsync` that allow retrieving the work item's return value. By [@jcparkyn](https://github.com/jcparkyn). + +## [1.25.3] - 2023-11-14 + +- Upgrade all .NET 8 preview dependencies to .NET 8 stable. + +## [1.24.10] - 2023-10-14 + +### Fixed + +- When the `TestContext` was disposed, it disposed of all services via the service provider. However, if there were ongoing renders happening, this could cause inconsistent state in the render tree, since the `TestRenderer` could try to access the service provider to instantiate components. + This release changes the dispose phase such that the renderer gets disposed first, then the service provider. The disposal of any services that implement `IAsyncDisposable` is now also awaited. Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). Reported by [@BenSchoen](https://github.com/BenSchoen) in . + +### Added + +- Support for custom service provider factories (`IServiceProviderFactory`). This enables the use of Autofac and other frameworks for dependency injection like on real-world ASP.NET Core / Blazor projects. By [@inf9144](https://github.com/inf9144). + +- Ability to raise the `oncancel` and `onclose` event, that was introduced with .NET 8. + +## [1.23.9] - 2023-09-06 + +### Fixed + +- If the renderer was not idle when calling `SetParametersAndRender`, the method could return before the parameters were set and the component under test had finished rendering. This was a regression that happened in v1.21.9. Reported by [@Skintkingle](https://github.com/Skintkingle]) in . Fixed by [@egil](https://github.com/egil). + +### Added + +- `net8.0` support +- Increased timeout of `WaitForAssertion` to infinite when a debugger is attached. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixed + +- AngleSharp IElement extension methods do not work with `IRenderedFragment.Find`. Reported by [a2er](https://github.com/a2er). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.22.19] - 2023-07-28 + +### Added + +- Update bunit templates to support the target framework version of the project. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixed + +- Calling `MarkupMatches(RenderFragment)` from a lambda passed to e.g. `WaitForAssertion` could lead to a deadlock in certain circumstances. Fixed by [@linkdotnet](https://github.com/linkdotnet). Reported by [@uecasm](https://github.com/uecasm) in . + +- Rendering complex component hierarchies could result in a stack overflow. Fixed by [@egil](https://github.com/egil).. Reported by [@groogiam](https://github.com/groogiam) in . + +- Remove capturing and dispatching markup updates to test frameworks synchronization context again. This could cause deadlocks and does not have any impact on test stability. Fixed by [@egil](https://github.com/egil). Reported by [@biohazard999](https://github.com/biohazard999) in . + +## [1.21.9] - 2023-07-02 + +### Fixed + +- Allow using 3rd party `IComponentActivator` at the same time as component factories. By [@egil](https://github.com/egil). Reported by [BenSchoen](https://github.com/BenSchoen) in . + +- Calling `IRenderedComponent.Render()` or `IRenderedComponent.SetParametersAndRender()` did not batch up multiple synchronous re-renders after setting parameters. This is now changed such that the method causes the component to re-render with new parameters in the same way as if a parent component had passed new parameters to it. By [@egil](https://github.com/egil). Reported by [@Jcparkyn](https://github.com/Jcparkyn) in . + +## [1.20.8] - 2023-05-21 + +### Added + +- Added static `DefaultWaitTimeout` property to `TestContext` to enable overriding the default timeout of "wait for" methods like `WaitForAssertion` from 1 second to something else. By [@egil](https://github.com/egil). + +### Fixed + +- TestRenderer throws `ObjectDisposedException` if any methods is accessed after it has been disposed. It will also prevent changes to the internal render tree after it has been disposed. By [@egil](https://github.com/egil). + +## [1.19.14] - 2023-04-26 + +### Fixed + +- Custom elements with attributes throw `ArgumentException` with `MarkupMatches`. Reported by [@candritzky](https://github.com/candritzky). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +### Changed + +- Changed test renderer such that updates to rendered components markup happen in the same synchronization context as the test framework is using (if any), if any, to avoid memory race conditions. By [@egil](https://github.com/egil). + +## [1.18.4] - 2023-02-26 + +### Fixed + +- Some characters where not properly escaped. Reported by [@pwhe23](https://github.com/pwhe23). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Clicking a submit button or submit input element inside a form, submits the form, if the submit button or submit input element does not have the `@onclick:preventDefault` attribute set. Reported by [@linkdotnet](https://github.com/linkdotnet). Fixed by [@egil](https://github.com/egil). + +## [1.17.2] - 2023-02-22 + +- Submit buttons and input fields now no longer cause a form submit when they have the `@onclick:preventDefault` attribute. By [@JelleHissink](https://github.com/JelleHissink). + +## [1.16.2] - 2023-02-07 + +- Changed semantic comparer to handle elements parsed outside their proper context, e.g. an `` element parsed without being inside a `` element. The semantic comparer will now be able to treat those as regular elements and thus be able to compare correctly to other elements of the same type and with the same node name. By [@egil](https://github.com/egil). + +## [1.15.5] - 2023-02-04 + +- Upgrade AngleSharp.Diffing to 0.17.1. + +## [1.14.4] - 2023-01-11 + +### Added + +- Added `IMemoryCache` by default to the Services container. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixed + +- Added support in `FakeNavigationManager` to handle umlauts. +- Fixed a bug where attribute values did not get escaped. Reported by [@brettwinters](https://github.com/brettwinters). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.13.5] - 2022-12-16 + +This release contains a bunch of small tweaks and fixes. + +## [1.12.6] - 2022-11-08 + +### Fixed + +- The created HTML contained encoded strings. Reported by [@tobiasbrandstaedter](https://github.com/tobiasbrandstaedter). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.11.7] - 2022-10-13 + +### Added + +- Added the `StateFromJson` method to the `NavigationHistory` type, to make it easy to deserialize navigation state stored as JSON during a call to `NavigationManager.NavigateTo`, e.g. as seen with the new `InteractiveRequestOptions` type available in .NET 7. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). + +## [1.10.14] - 2022-09-16 + +### Added + +- Added new test double `FakeWebAssemblyHostEnvironment` that implements `IWebAssemblyHostEnvironment`. By [@KristofferStrube](https://github.com/KristofferStrube). + +- Added `Bind` method to parameter builder that makes it easier to emulate the `@bind-Value` syntax in C#-based tests. + + When writing tests in razor files, the `@bind-` directive can be directly applied like this: + + ```razor + + ``` + + The same expression in C# syntax is more verbose like this: + + ```csharp + RenderComponent(ps => ps + .Add(c => c.Value, value) + .Add(c => c.ValueChanged, newValue => value = newValue) + .Add(c => c.ValueExpression, () => value)); + ``` + + With the new `Bind` method this can be done in one method: + + ```csharp + RenderComponent(ps => ps + .Bind(c => c.Value, value, newValue => value = newValue, () => value)); + ``` + + By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). + +- Added support for `NavigationLock`, which allows user code to intercept and prevent navigation. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). + +### Fixed + +- `JSInterop.VerifyInvoke` reported the wrong number of actual invocations of a given identifier. Reported by [@otori](https://github.com/otori). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.9.8] - 2022-06-07 + +### Changed + +- `WaitForAssertion` method is now marked as an assertion method with the `[AssertionMethod]` attribute. This makes certain analyzers like SonarSource's [Tests should include assertions](https://rules.sonarsource.com/csharp/RSPEC-2699) happy. By [@egil](https://github.com/egil). + +### Fixes + +- A race condition existed between `WaitForState` / `WaitForAssertion` and `FindComponents`, if the first used the latter. Reported by [@rmihael](https://github.com/rmihael), [@SviatoslavK](https://github.com/SviatoslavK), and [@RaphaelMarcouxCTRL](https://github.com/RaphaelMarcouxCTRL). Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). + +- Triggering of event handlers now runs entirely inside the renderers synchronization context, avoiding race condition between elements in the DOM tree being updated by the renderer and the event triggering logic traversing the DOM tree to find event handlers to trigger. Reported by [@FlukeFan](https://github.com/FlukeFan). Fixed by [@egil](https://github.com/egil). + +## [1.8.15] - 2022-05-19 + +### Added + +- Added test helpers that make it much easier to pass files to the `InputFile` component. Learn more [in the documentation](https://bunit.dev/docs/test-doubles/input-file). By [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). + +### Changed + +- `Htmlizer` uses `StringBuilder` instead of `List` to reduce allocations and improve render speed. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixes + +- `TestServiceProvider` now implements `IAsyncDisposable`. This means `TestContext.Dispose()` now calls the async disposable method as well as the non-async version on the service provider. It does however not block or await the task returned, since that can lead to deadlocks. + + To await the disposal of async services registered in the `TestContext.Services` container, do the following: + + 1. Create a new type that derives from `TestContext` and which implement `IAsyncDisposable`. + 2. In the `DisposeAsync()` method, call `Services.DisposeAsync()`. + 3. Override the `Dispose` and have it only call `Services.Dispose()`. + + Reported by [@vedion](https://github.com/vedion) and fixed by [@egil](https://github.com/egil). + +## [1.7.7] - 2022-04-29 + +### Added + +- Added method `SetAuthenticationType` to `TestAuthorizationContext` to allow for custom authentication type checks. By [@TimPurdum](https://github.com/timpurdum). + +- Added `DisposeComponents` to `TestContextBase`. It will dispose and remove all components rendered by the `TestContextBase`. By [@linkdotnet](https://github.com/linkdotnet). + +- Added .NET 7 as a target framework for bUnit. By [@linkdotnet](https://github.com/linkdotnet). + +### Fixed + +- Fixed step by step guide for building and viewing the documentation locally. By [@linkdotnet](https://github.com/linkdotnet). + +- `FakeNavigationManager.NavigateTo` could lead to exceptions when navigating to external url's. Reported by [@TDroogers](https://github.com/TDroogers). Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.6.4] - 2022-02-22 + +A quick minor release that primiarily fixes a regression in 1.5.12. + +### Fixed + +- `ClickAsync` could lead to bubbling exceptions from `GetDispatchEventTasks` even though they should be handled. Reported by [@aguacongas](aguacongas). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Added more non bubbling events to bUnit so it behaves closer to the HTML specification. [@linkdotnet](https://github.com/linkdotnet). + +## [1.5.12] - 2022-02-15 + +This first release of 2022 includes one fix and four additions. A huge thank you to [Steven Giesel (@linkdotnet)](https://github.com/linkdotnet) and [Denis Ekart (@denisekart)](https://github.com/denisekart) for their contributions to this release. + +Also a big shout out to **bUnit's sponsors** who helped make this release happen. + +**The higher tier sponsors are:** + +- [Progress Telerik](https://github.com/Progress-Telerik) +- [Syncfusion](https://github.com/syncfusion) +- [CTRL Informatique](https://github.com/CTRL-Informatique) + +**Other sponsors are:** + +- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) +- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) +- [Domn Werner (@domn1995)](https://github.com/domn1995) +- [Mladen Macanović (@stsrki)](https://github.com/stsrki) +- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) +- [Steven Giesel (@linkdotnet)](https://github.com/linkdotnet) + +### Added + +- Added `FakeSignOutSessionStateManage` type in Blazor, that makes it easy to test components that use the `SignOutSessionStateManage` type. By [@linkdotnet](https://github.com/linkdotnet). +- Added a validation to `AddChildContent` method in `ComponentParameterCollectionBuilder` that will throw an exception if the component's `ChildContent` is a generic type. By [@denisekart](https://github.com/denisekart). +- Added more optional arguments for `Click` and `DoubleClick` extensions which were introduced in .NET 5 and .NET 6. By [@linkdotnet](https://github.com/linkdotnet). +- Added template support for `Nunit` and `MSTest` unit test frameworks. By [@denisekart](https://github.com/denisekart). + +### Fixed + +- Changed `GetDispatchEventTasks` for bubbling events such that handled exceptions are not rethrown later from the `WaitFor...` helpers methods. Reported by [@AndrewStrickland](https://github.com/AndrewStrickland). Fixed by [@linkdotnet](https://github.com/linkdotnet) + +## [1.4.15] - 2021-12-18 + +This release reintroduces `Stub` and related back into the main library, so the "preview" library `bunit.web.mock` is already obsolete. + +A big shout out to **bUnit's sponsors** who helped make this release happen. + +**The higher tier sponsors are:** + +- [Progress Telerik](https://github.com/Progress-Telerik) +- [Syncfusion](https://github.com/syncfusion) +- [CTRL Informatique](https://github.com/CTRL-Informatique) + +**Other sponsors are:** + +- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) +- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) +- [Domn Werner (@domn1995)](https://github.com/domn1995) +- [Mladen Macanović (@stsrki)](https://github.com/stsrki) +- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) + +### Added + +- Add `ComponentFactories` extensions method that makes it easy to register an instance of a replacement component. By [@egil](https://github.com/egil). +- Add ability to pass `ServiceProviderOptions` to `TestServiceProvider` through property to allow users to customize the service provider. By [@rodolfograve](https://github.com/rodolfograve). + +### Fixed + +- Changed `SetParametersAndRender` such that it rethrows any exceptions thrown by the component under tests `SetParametersAsync` method. Thanks to [@bonsall](https://github.com/bonsall) for reporting the issue. Fixed by [@egil](https://github.com/egil). +- `onclick` on a button inside a form will raise the `onsubmit` event for the form itself. Reported by [@egil]. Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Only forms are allowed to have a `onsubmit` event handler. When `onsubmit` is invoked from a non-form element results in an exception. Fixed by [@linkdotnet](https://github.com/linkdotnet). + +## [1.3.42] - 2021-11-09 + +This release includes support for .NET 6, with support for all new features in Blazor with that release. There are also a number of additions and fixes, all listed below. + +Big shout out to **bUnit's sponsors** who helped make this release happen. + +**The higher tier sponsors are:** + +- [Progress Telerik](https://github.com/Progress-Telerik) +- [Syncfusion](https://github.com/syncfusion) + +**Other sponsors are:** + +- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) +- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) +- [Domn Werner (@domn1995)](https://github.com/domn1995) +- [Mladen Macanović (@stsrki)](https://github.com/stsrki) +- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) + +### Added + +List of added functionality in this release. + +- Added support for writing tests of components that use the `` component included in .NET 6. This includes an assertion helper method `VerifyFocusOnNavigateInvoke` on bUnit's `JSInterop` that allow you to verify that `` has set focus on an element during render. For example, to verify that `h1` selector was used to pick an element to focus on, do: + + ```csharp + // component uses + var cut = RenderComponent(); + + // Verifies that called it's JavaScript function + var invocation = JSInterop.VerifyFocusOnNavigateInvoke(); + + // Verify that the invocation of JavaScript function included the "h1" as the selector + Assert.Equal("h1", invocation.Arguments[0]); + ``` + + By [@egil](https://github.com/egil). + +- Added fake version of the `PersistentComponentState` type in Blazor that makes it possible to test components that use the type. By [@egil](https://github.com/egil). + +- Added `TriggerEvent` method to make it easier to trigger custom events. By [@egil](https://github.com/egil). + +- Added `History` capture in the `FakeNavigationManager`. By [@egil](https://github.com/egil). + +- Added new bUnit component mocking library, available via NuGet as `bunit.web.mock`. It is currently in preview and the features/APIs of it will change! + +- Added `WaitForElement` and `WaitForElements` methods. These makes it possible to wait for one or more elements to appear in the DOM before continuing a test, similar to how `WaitForAssertion` allows you to wait for an assertion to pass, or `WaitForState` allows you to wait for a predicate to pass. By [@egil](https://github.com/egil). + +### Changed + +- Added automatic conversion of values (types) passed to `Change()` and `Input()` event trigger methods. This means that e.g. a `DateTime` passed to `Change()` is automatically converted to a string format that Blazor expects. By [@egil](https://github.com/egil). + +### Fixed + +- The `Click` and `DoubleClick` extension methods now set the `MouseEventArgs.Detail` property to `1` and `2` respectively by default, unless the user specifies something else. This makes the methods more correctly emulate how Blazor reports single or double clicks on an element in the browser. Thanks to [@David-Moreira](https://github.com/David-Moreira) for the help troubleshooting this issue. By [@egil](https://github.com/egil). + +- `FocusAsync()` method handler on `ElementReference` and `` js handler return completed `Task`. By [@anddrzejb](https://github.com/anddrzejb). + +- Fixes handling of disposed event handlers of bubbling events. See issue [#518](https://github.com/bUnit-dev/bUnit/issues/518) for details. Thanks to [@David-Moreira](https://github.com/David-Moreira) for helping debug this issue. + +- Async event trigger methods are not public. In most circumstances you do not need to use them, but if you have a scenario where you want to check that something has not happened after an event handler was triggered, then you can use the async methods and await them to know when they are completed. See [#552](https://github.com/bUnit-dev/bUnit/discussions/552) for details. By [@egil](https://github.com/egil). + +## [1.2.49] - 2021-08-09 + +### Added + +List of added functionality in this release. + +- Added more extensions methods to `MarkupMatchesAssertExtensions` to allow asserting with `MarkupMatches` on `IEnumerable` and `IElement`. By [@jgoday](https://github.com/jgoday). + +- Added `BunitErrorBoundaryLogger` implementation of `IErrorBoundaryLogger` (needed for Blazor's ErrorBoundary component in .NET 6.0). By [@jgoday](https://github.com/jgoday). + +- Added `ComponentFactories` property to the `TestContextBase` type. The `ComponentFactories` property is a `ComponentFactoryCollection` type that contains `IComponentFactory` types. These are used by bUnits component activator, whenever a component is created during testing. If no component factories is added to the collection, the standard component activator mechanism from Blazor is used. This feature makes it possible to control what components are created normally during a test, and which should be e.g. replaced by a test dummy. More info is available in issue [#388](https://github.com/bUnit-dev/bUnit/issues/388). + Learn more about this feature on the [Controlling component instantiation](https://bunit.dev/docs/providing-input/controlling-component-instantiation) page. + +- Added `HasComponent()` to `IRenderedFragement`. Use it to check if the rendered fragment contains a component of type `TComponent`. Added by [@egil](https://github.com/egil). + +- Added `AddStub` and `Add` extension methods to `ComponentFactories` that makes it easy to configure bUnit to replace components in the render tree with stubs. Both methods have overloads that allow for fine grained selection of component types to "double" during testing. Added by [@egil](https://github.com/egil) in [#400](https://github.com/bUnit-dev/bUnit/pull/400). + +### Changed + +List of changes in this release. + +- Updated AngleSharp and related libraries to 0.16.0. _NOTE, the new version of AngleSharp includes nullable annotations, which might affect how your code compiles, if you have nullable checking enabled in your test project._ By [@egil](https://github.com/egil). + +- Updated .NET 6 dependencies to preview 5. By [@egil](https://github.com/egil). + +### Fixed + +List of fixes in this release. + +- Fixed JSInterop error message when trying to import an unconfigured module. By [@jgoday](https://github.com/jgoday) in [#425](https://github.com/bUnit-dev/bUnit/pull/425). + +- Fixed issue where a registered fall-back service provider was not made available to resolve service dependencies of components under test. Thanks to [@dady8889](https://github.com/dady8889) for the reporting the issue. + +- Fixed handling of escaped uri's in FakeNavigationManager. By [@linkdotnet](https://github.com/linkdotnet) in [#460](https://github.com/bUnit-dev/bUnit/pull/460). + +- Captured error message from event dispatcher in renderer that would previously be hidden from the user. Related to issue [#399](https://github.com/bUnit-dev/bUnit/issues/399). + +## [1.1.5] - 2021-04-30 + +### Added + +- All bUnit assemblies is now strong named signed. + +- Added .NET 6 (preview 3) as a target framework for bUnit, bUnit.core and bUnit.web. + +### Changed + +- Changed bunit.template such that created projects only reference the bUnit package. Bumped other referenced packages to latest version. + +- Changed TestServiceProvider to validate scopes of registered services, such that it behaves like the service provider (default IoC container) in Blazor. + +## [1.0.16] + +The following section list all changes since preview 02. + +### Changed + +List of changes in existing functionality. + +- _**BREAKING CHANGE:**_ Writing tests using the test components `` and `` components inside .razor files has been moved to its own library, `bunit.web.testcomponents`. This was done for several reasons: + + - The feature has been experimental since it was introduced, and it was introduced get a more natural way of specifying the component under test and any related markup used by test. + - The feature is only supported with xUnit. + - There are some issues related to the `SourceFileFinder` library, which is used to discover the test components. + - A better way of writing tests in .razor files has been added to bUnit, using _"inline render fragments"_. This method works with all general purpose test frameworks, e.g. MSTest, NUnit, and xUnit, is more flexible, and offer less boilerplate code than the test components. The bUnit documentation has been updated with a guide to this style. + + The new package `bunit.web.testcomponents` is provided as is, without expectation of further development or enhancements. If you are using the test components currently for writing tests, it will continue to work for you. If you are starting a new project, or have few of these tests, consider switching to the "inline render fragments" style. + + Here is a quick comparison of the styles, using a very simple component. + + First, the test component style: + + ```razor + @inherits TestComponentBase + + + + + + + @code + { + void HelloWorldComponentRendersCorrectly(Fixture fixture) + { + // Act + var cut = fixture.GetComponentUnderTest(); + + // Assert + cut.MarkupMatches("

Hello world from Blazor

"); + } + } + + + + + + + +

Hello world from Blazor

+
+
+ ``` + + The a single test in "inline render fragments" style covers both cases: + + @inherits TestContext + @code { + [Fact] + public void HelloWorldComponentRendersCorrectly() + { + // Act + var cut = Render(@); + + // Assert + cut.MarkupMatches(@

Hello world from Blazor

); + } + } + + To make the snapshot test scenario even more compact, consider putting all code in one line, e.g. `Render(@).MarkupMatches(@

Hello world from Blazor

);`. + + For a more complete snapshot testing experience, I recommend looking at Simon Cropp's [Verify](https://github.com/VerifyTests) library, in particular the [Verify.Blazor extension to bUnit](https://github.com/VerifyTests/Verify.Blazor#verifybunit). Verify comes with all the features you expect from a snapshot testing library. + +### Removed + +List of now removed features. + +- The `AddXunitLogger` method, which provided support for capturing `ILogger` messages and passing them to xUnit's `ITestOutputHelper`, has been removed. There were no need to keep xUnit specific code around in bUnit going forward, and there are many implementations on-line that supports this feature, so having it in bUnit made little sense. One such alternative, which bUnit has adopted internally, is to use Serilog. This looks as follows: + + 1. Add the following packages to your test project: `Serilog`, `Serilog.Extensions.Logging`, and `Serilog.Sinks.XUnit`. + 2. Add the following class/extension method to your test project (which replicates the signature of the removed `AddXunitLogger` method): + + ```csharp + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using Serilog; + using Serilog.Events; + using Xunit.Abstractions; + + namespace Bunit + { + public static class ServiceCollectionLoggingExtensions + { + public static IServiceCollection AddXunitLogger(this IServiceCollection services, ITestOutputHelper outputHelper) + { + var serilogLogger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.TestOutput(outputHelper, LogEventLevel.Verbose) + .CreateLogger(); + + services.AddSingleton(new LoggerFactory().AddSerilog(serilogLogger, dispose: true)); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + + return services; + } + } + } + ``` + +- The `bunit.xunit` package has been removed, since it is no longer needed (there is no code left in it). + +## [1.0.0-preview-02] - 2021-03-26 + +The following section list all changes in 1.0.0 preview 02. + +The plan is to make this the last preview release of bUnit. If no big blocking bugs show up the next two weeks, a non-preview release of bUnit will be pushed out to the world. + +### Added + +List of new features. + +- Added the ability to pass a "fallback `IServiceProvider`" to the `TestServiceProvider`, available through the `Services` property on a `TestContext`. The fallback service provider enables a few interesting scenarios, such as using an alternative IoC container, or automatically generating mocks of services components under test depend on. See the [Injecting Services into Components Under Test page](https://bunit.egilhansen.com/docs/providing-input/inject-services-into-components) for more details on this feature. By [@thopdev](https://github.com/thopdev) in [#310](https://github.com/egil/bUnit/issues/310). + +- Added `Task ITestRenderer.UnhandledException` property that returns a `Task` that completes when the renderer captures an unhandled exception from a component under test. If a component is missing exception handling of asynchronous operations, e.g. in the `OnInitializedAsync` method, the exception will not break the test, because it happens on another thread. To have a test fail in this scenario, you can await the `UnhandledException` property on the `TestContext.Renderer` property, e.g.: + + ```csharp + using var ctx = new TestContext(); + + var cut = ctx.RenderComponent(); + + Task waitTimeout = Task.Delay(500).ContinueWith(_ => Task.FromResult(null)).Unwrap(); + Exception? unhandledException = await Task.WhenAny(Renderer.UnhandledException, waitTimeout).Unwrap(); + + Assert.Null(unhandledException); + ``` + + In this example, we await any unhandled exceptions from the renderer, or our wait timeout. The `waitTimeout` ensures that we will not wait forever, in case no unhandled exception is thrown. + + NOTE, a better approach is to use the `WaitForState` or `WaitForAssertion` methods, which now also throws unhandled exceptions. Using them, you do not need to set up a wait timeout explicitly. + + By [@egil](https://github.com/egil) in [#344](https://github.com/egil/bUnit/issues/344). + +- Added a simple fake navigation manager, which is registered by default in bUnit's service provider. When the fake navigation manager's `NavigateTo` method is called, it does two things: + + 1. Set the `Uri` property to the URI passed to the `NavigateTo` method (with the URI normalized to an absolute URI). + 2. Raise the `LocationChanged` event with the URI passed to the `NavigateTo` method. + + Lets look at an example: To verify that the `` component below calls the `NavigationManager.NavigateTo` method with the expected value, do the following: + + `` component: + + ```cshtml + @inject NavigationManager NavMan + @code { + protected override void OnInitialized() + { + NavMan.NavigateTo("foo"); + } + } + ``` + + Test code: + + ```csharp + // Arrange + using var ctx = new TestContext(); + var navMan = ctx.Services.GetRequiredService(); + + // Act + var cut = ctx.RenderComponent(); + + // Assert + Assert.Equal($"{navMan.BaseUri}foo", navMan.Uri); + ``` + + Since the `foo` input argument is normalized to an absolute URI, we have to do the same normalization in our assertion. + + The fake navigation manager's `BaseUri` is set to `http://localhost/`, but it is not recommended to use that URL directly in your code. Instead create an assertion by getting that value from the `BaseUri` property, like shown in the example above. + + By [@egil](https://github.com/egil) in [#345](https://github.com/egil/bUnit/pull/345). + +- Added additional bUnit JSInterop `Setup` methods, that makes it possible to get complete control of invocation matching for the created handler. By [@egil](https://github.com/egil). + +### Changed + +List of changes in existing functionality. + +- `WaitForAssertion` and `WaitForState` now throws unhandled exception caught by the renderer from a component under test. This can happen if a component is awaiting an asynchronous operation that throws, e.g. a API call using a misconfigured `HttpClient`. By [@egil](https://github.com/egil) in [#310](https://github.com/egil/bUnit/issues/344). + +- Improvements to error message from bUnit's JSInterop when it receives an invocation that it has not been set up to handle. By [@egil](https://github.com/egil) in [#346](https://github.com/egil/bUnit/pull/346). + +### Removed + +List of now removed features. + +### Fixed + +List of any bug fixes. + +## [1.0.0-preview-01] - 2020-12-24 + +The following section list all changes in 1.0.0 preview 01. + +### Added + +List of new features. + +- Added support for casting `BUnitJSRuntime` to `IJSInProcessRuntime` and `IJSUnmarshalledRuntime`. By [@KristofferStrube](https://github.com/KristofferStrube) in [#279](https://github.com/egil/bUnit/pull/279) + +- Added support for triggering `@ontoggle` event handlers through a dedicated `Toggle()` method. By [@egil](https://github.com/egil) in [#256](https://github.com/egil/bUnit/pull/256). + +- Added out of the box support for `` component. When a `` component is used in a component under test, it's JavaScript interop-calls are faked by bUnits JSInterop, and it should result in all items being rendered immediately. By [@egil](https://github.com/egil) in [#240](https://github.com/egil/bUnit/issues/240). + +- Added support for components that call `ElementReference.FocusAsync`. These calls are handled by the bUnits JSInterop, that also allows you to verify that `FocusAsync` has been called for a specific element. For example, if a component has rendered an `` element, then the following code will verify that it has been focused using `FocusAsync`: + + ```csharp + var cut = RenderComponent(); + + var input = cut.Find("input"); + + JSInterop.VerifyFocusAsyncInvoke() + .Arguments[0] // the first argument is the ElemenetReference + .ShouldBeElementReferenceTo(input); + ``` + + By [@egil](https://github.com/egil) in [#260](https://github.com/egil/bUnit/pull/260). + +- Added `Render(RenderFragment)` and `Render(RenderFragment)` methods to `TestContext`, as well as various overloads to the `MarkupMatches` methods, that also takes a `RenderFragment` as the expected value. + + The difference between the generic `Render` method and the non-generic one is that the generic returns an `IRenderedComponent`, whereas the non-generic one returns a `RenderedFragment`. + + Calling `Render(RenderFragent)` is equivalent to calling `Render(RenderFragment).FindComponent()`, e.g. it returns the first component in the render tree of type `TComponent`. This is different from the `RenderComponent()` method, where `TComponent` _is_ the root component of the render tree. + + The main usecase for these are when writing tests inside .razor files. Here the inline syntax for declaring render fragments make these methods very useful. + + For example, to tests the `` page/component that is part of new Blazor apps, do the following (inside a `CounterTest.razor` file): + + ```cshtml + @code + { + [Fact] + public void Counter_Increments_When_Button_Is_Clicked() + { + using var ctx = new TestContext(); + var cut = ctx.Render(@); + + cut.Find("button").Click(); + + cut.Find("p").MarkupMatches(@

Current count: 1

); + } + } + ``` + + Note: This example uses xUnit, but NUnit or MSTest works equally well. + + In addition to the new `Render` methods, a empty `BuildRenderTree` method has been added to the `TestContext` type. This makes it possible to inherit from the `TestContext` type in test components, removing the need for newing up the `TestContext` in each test. + + This means the test component above ends up looking like this: + + ```cshtml + @inherts TestContext + @code + { + [Fact] + public void Counter_Increments_When_Button_Is_Clicked() + { + var cut = Render(@); + + cut.Find("button").Click(); + + cut.Find("p").MarkupMatches(@

Current count: 1

); + } + } + ``` + + Tip: If you have multiple test components in the same folder, you can add a `_Imports.razor` file inside it and add the `@inherits TestContext` statement in that, removing the need to add it to every test component. + + By [@egil](https://github.com/egil) in [#262](https://github.com/egil/bUnit/pull/262). + +- Added support for `IJSRuntime.InvokeAsync(...)` calls from components. There is now a new setup helper methods for configuring how invocations towards JS modules should be handled. This is done with the various `SetupModule` methods available on the `BunitJSInterop` type available through the `TestContext.JSInterop` property. For example, to set up a module for handling calls to `foo.js`, do the following: + + ```c# + using var ctx = new TestContext(); + var moduleJsInterop = ctx.JSInterop.SetupModule("foo.js"); + ``` + + The returned `moduleJsInterop` is a `BunitJSInterop` type, which means all the normal `Setup` and `SetupVoid` methods can be used to configure it to handle calls to the module from a component. For example, to configure a handler for a call to `hello` in the `foo.js` module, do the following: + + ```c# + moduleJsInterop.SetupVoid("hello"); + ``` + + By [@egil](https://github.com/egil) in [#288](https://github.com/egil/bUnit/pull/288). + +- Added support for registering services in bUnits `Services` collection that implements `IAsyncDisposable`. Suggested by [@jmaillet](https://github.com/jmaillet) in [#249](https://github.com/egil/bUnit/issues/249). + +### Changed + +List of changes in existing functionality. + +- bUnit's mock IJSRuntime has been moved to an "always on" state by default, in strict mode, and is now available through `TestContext`'s `JSInterop` property. This makes it possible for first party Blazor components like the `` component, which depend on JSInterop, to "just work" in tests. + + **Compatible with previous releases:** To get the same effect as calling `Services.AddMockJSRuntime()` in beta-11, which used to add the mock IJSRuntime in "loose" mode, you now just need to change the mode of the already on JSInterop, i.e. `ctx.JSInterop.Mode = JSRuntimeMode.Loose`. + + **Inspect registered handlers:** Since the new design allows registering invoke handlers in the context of the `TestContext`, you might need to get already registered handlers in your individual tests. This can be done with the `TryGetInvokeHandler()` method, that will return handler that can handle the parameters passed to it. E.g. to get a handler for a `IJSRuntime.InvokaAsync("getValue")`, call `ctx.JSInterop.TryGetInvokeHandler("getValue")`. + + Learn more [issue #237](https://github.com/egil/bUnit/issues/237). By [@egil](https://github.com/egil) in [#247](https://github.com/egil/bUnit/pull/247). + +- The `Setup(string identifier, Func, bool> argumentsMatcher)` and `SetupVoid(string identifier, Func, bool> argumentsMatcher)` methods in bUnits JSInterop/MockJSRuntime has a new second parameter, an `InvocationMatcher`. + + The `InvocationMatcher` type is a delegate that receives a `JSRuntimeInvoation` and returns true. The `JSRuntimeInvoation` type contains the arguments of the invocation and the identifier for the invocation. This means old code using the `Setup` and `SetupVoid` methods should be updated to use the arguments list in `JSRuntimeInvoation`, e.g., change the following call: + + `ctx.JSInterop.Setup("foo", args => args.Count == 2)` to this: + `ctx.JSInterop.Setup("foo", invocation => invocation.Arguments.Count == 2)`. + + Changed added in relation to [#240](https://github.com/egil/bUnit/issues/240) in [#257](https://github.com/egil/bUnit/issues/257) by [@egil](https://github.com/egil). + +- Changed `AddTestAuthorization` such that it works in Razor-based test contexts, i.e. on the `Fixture` and `SnapshotTest` types. + +### Removed + +List of now removed features. + +- A few bUnit internal xUnit assert helper methods, the custom `ShouldAllBe` methods, has mistakingly been part of the bunit.xunit package. These have been removed. + +### Fixed + +List of any bug fixes. + +- When an `Add` call to the component parameter collection builder was used to select a parameter that was inherited from a base component, the builder incorrectly reported the selected property/parameter as missing on the type. Reported by [@nickmuller](https://github.com/nickmuller) in [#250](https://github.com/egil/bUnit/issues/250). + +- When an element, found in the DOM tree using the `Find()`, method was removed because of an event handler trigger on it, e.g. an `cut.Find("button").Click()` event trigger method, an `ElementNotFoundException` was thrown. Reported by [@nickmuller](https://github.com/nickmuller) in [#251](https://github.com/egil/bUnit/issues/251). + +- In the built-in fake authentication system in bUnit, roles and claims were not available in components through the a cascading parameter of type `Task`. Reported by [@AFAde](https://github.com/AFAde) in [#253](https://github.com/egil/bUnit/discussions/253) and fixed in [#291](https://github.com/egil/bUnit/pull/291) by [@egil](https://github.com/egil). + +## [1.0.0-beta 11] - 2020-10-26 + +The following section list all changes in beta-11. + +### Added + +List of new features. + +- Two new overloads to the `RenderFragment()` and `ChildContent()` component parameter factory methods have been added that takes a `RenderFragment` as input. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). + +- Added a `ComponentParameterCollection` type. The `ComponentParameterCollection` is a collection of component parameters, that knows how to turn those components parameters into a `RenderFragment`, which will render a component and pass any parameters inside the collection to that component. That logic was spread out over multiple places in bUnit, and is now owned by the `ComponentParameterCollection` type. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). + +- Added additional placeholder services for `NavigationManager`, `HttpClient`, and `IStringLocalizer`, to make it easier for users to figure out why a test is failing due to missing service registration before rendering a component. By [@joro550](https://github.com/joro550) in [#223](https://github.com/egil/bUnit/pull/223). + +- Added `Key` class that represents a keyboard key and helps to avoid constructing `KeyboardEventArgs` object manually. The key can be passed to `KeyPress`, `KeyDown`, or `KeyUp` helper methods to raise keyboard events. The `Key` class provides static special keys or can be obtained from character or string. Keys can be combined with key modifiers: `Key.Enter + Key.Alt`. + + For example, this makes it easier to trigger keyboard events on an element: + + ```csharp + var cut = ctx.RenderComponent(); + var element = cut.Find("input"); + + element.KeyDown(Key.Enter + Key.Control); // Triggers onkeydown event with Ctrl + Enter + element.KeyUp(Key.Control + Key.Shift + 'B'); // Triggers onkeyup event with Ctrl + Shift + B + element.KeyPress('1'); // Triggers onkeypress event with key 1 + element.KeyDown(Key.Alt + "<"); // Triggers onkeydown event with Alt + < + ``` + + By [@duracellko](https://github.com/duracellko) in [#101](https://github.com/egil/bUnit/issues/101). + +- Added support for registering/adding components to a test context root render tree, which components under test is rendered inside. This allows you to simplify the "arrange" step of a test when a component under test requires a certain render tree as its parent, e.g. a cascading value. + + For example, to pass a cascading string value `foo` to all components rendered with the test context, do the following: + + ```csharp + ctx.RenderTree>(parameters => parameters.Add(p => p.Value, "foo")); + var cut = ctx.RenderComponent(); + ``` + + By [@egil](https://github.com/egil) in [#236](https://github.com/egil/bUnit/pull/236). + +- Added "catch-all" `Setup` method to bUnit's mock JS runtime, that allows you to specify only the type when setting up a planned invocation. By [@nemesv](https://github.com/nemesv) in [#234](https://github.com/egil/bUnit/issues/234). + +### Changed + +List of changes in existing functionality. + +- The `ComponentParameterBuilder` has been renamed to `ComponentParameterCollectionBuilder`, since it now builds the `ComponentParameterCollection` type, introduced in this release of bUnit. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). + +- `ComponentParameterCollectionBuilder` now allows adding cascading values that is not directly used by the component type it targets. This makes it possible to add cascading values to children of the target component. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). + +- The `Add(object)` has been replaced by `AddCascadingValue(object)` in `ComponentParameterCollectionBuilder`, to make it more clear that an unnamed cascading value is being passed to the target component or one of its child components. It is also possible to pass unnamed cascading values using the `Add(parameterSelector, value)` method, which now correctly detect if the selected cascading value parameter is named or unnamed. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). + +- It is now possible to call the `Add()`, `AddChildContent()` methods on `ComponentParameterCollectionBuilder`, and the factory methods `RenderFragment()`, `ChildContent()`, and `Template()`, _**multiple times**_ for the same parameter, if it is of type `RenderFragment` or `RenderFragment`. Doing so previously would either result in an exception or just the last passed `RenderFragment` to be used. Now all the provided `RenderFragment` or `RenderFragment` will be combined at runtime into a single `RenderFragment` or `RenderFragment`. + + For example, this makes it easier to pass e.g. both a markup string and a component to a `ChildContent` parameter: + + ```csharp + var cut = ctx.RenderComponent(parameters => parameters + .AddChildContent("

Below you will find a most interesting alert!

") + .AddChildContent(childParams => childParams + .Add(p => p.Heading, "Alert heading") + .Add(p => p.Type, AlertType.Warning) + .AddChildContent("

Hello World

") + ) + ); + ``` + + By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). + +- All test doubles are now in the same namespace, `Bunit.TestDoubles`. So all import statements for `Bunit.TestDoubles.JSInterop` and `Bunit.TestDoubles.Authorization` must be changed to `Bunit.TestDoubles`. By [@egil](https://github.com/egil) in [#223](https://github.com/egil/bUnit/pull/223). + +- Marked MarkupMatches methods as assertion methods to stop SonarSource analyzers complaining about missing assertions in tests. By [@egil](https://github.com/egil) in [#229](https://github.com/egil/bUnit/pull/229). + +- `AddTestAuthorization` now extends `TestContext` instead of `TestServiceProvider`, and also automatically adds the `CascadingAuthenticationState` component to the root render tree. [@egil](https://github.com/egil) in [#237](https://github.com/egil/bUnit/pull/367). + +### Removed + +List of now removed features. + +- The async event dispatcher helper methods have been removed (e.g. `ClickAsync()`), as they do not provide any benefit. If you have an event that triggers async operations in the component under test, instead use `cut.WaitForState()` or `cut.WaitForAssertion()` to await the expected state in the component. + +### Fixed + +List of any bug fixes. + +- Using the ComponentParameterCollectionBuilder's `Add(p => p.Param, value)` method to add a unnamed cascading value didn't create an unnnamed cascading value parameter. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). Credits to [Ben Sampica (@benjaminsampica)](https://github.com/benjaminsampica) for reporting and helping investigate this issue. +- Triggered events now bubble correctly up the DOM tree and triggers other events of the same type. This is a **potentially breaking change,** since this changes the behaviour of event triggering and thus you might see tests start breaking as a result hereof. By [@egil](https://github.com/egil) in [#119](https://github.com/egil/bUnit/issues/119). + +## [1.0.0-beta 10] - 2020-09-15 + +The following section list all changes in beta-10. + +### Added + +List of new features. + +- Added support for .NET 5 RC-1. + +### Changed + +List of changes in existing functionality. + +- Related to [#189](https://github.com/egil/bUnit/issues/189), a bunch of the core `ITestRenderer` and related types have changed. The internals of `ITestRenderer` is now less exposed and the test renderer is now in control of when rendered components and rendered fragments are created, and when they are updated. This enables the test renderer to protect against race conditions when the `FindComponent`, `FindComponents`, `RenderFragment`, and `RenderComponent` methods are called. + +### Fixed + +List of any bug fixes. + +- Fixes [#189](https://github.com/egil/bUnit/issues/189): The test renderer did not correctly protect against a race condition during initial rendering of a component, and that could in some rare circumstances cause a test to fail when it should not. This has been addressed in this release with a major rewrite of the test renderer, which now controls and owns the rendered component and rendered fragment instances which is created when a component is rendered. By [@egil](https://github.com/egil) in [#201](https://github.com/egil/bUnit/pull/201). Credits to [@Smurf-IV](https://github.com/Smurf-IV) for reporting and helping investigate this issue. + +## [1.0.0-beta-9] - 2020-08-26 + +This release contains a couple of fixes, and adds support for .NET Preview 8 and later. There are no breaking changes in this release. + +Thanks to [pharry22](https://github.com/pharry22) for submitting fixes and improvements to the documentation. + +### Added + +List of new features. + +- Added `InvokeAsync(Func)` to `RenderedComponentInvokeAsyncExtensions`. By [@JeroenBos](https://github.com/JeroenBos) in [#151](https://github.com/egil/bUnit/pull/177). +- Added `ITestRenderer Renderer { get ; }` to `IRenderedFragment` to make it possible to simplify the `IRenderedComponentBase` interface. By [@JeroenBos](https://github.com/JeroenBos) in [#151](https://github.com/egil/bUnit/pull/177). +- Added support for scoped CSS to `MarkupMatches` and related comparer methods. By [@egil](https://github.com/egil) in [#195](https://github.com/egil/bUnit/pull/195). + +### Changed + +List of changes in existing functionality. + +- Moved `InvokeAsync()`, `Render()` and `SetParametersAndRender()` methods out of `IRenderedComponentBase` into extension methods. By [@JeroenBos](https://github.com/JeroenBos) in [#151](https://github.com/egil/bUnit/pull/177). +- Accessing `Markup`, `Nodes` and related methods on a rendered fragment whose underlying component has been removed from the render tree (disposed) now throws a `ComponentDisposedException`. By [@egil](https://github.com/egil) in [#184](https://github.com/egil/bUnit/pull/184). +- Changed bUnit's build to target both .net 5.0 and .net standard 2.1. By [@egil](https://github.com/egil) in [#187](https://github.com/egil/bUnit/pull/187). + +### Fixed + +List of any bug fixes. + +- Fixes [#175](https://github.com/egil/bUnit/issues/175): When a component referenced in a test, e.g. through the `FindComponent()` method was removed from the render tree, accessing the reference could caused bUnit to look for updates to it in the renderer, causing a exception to be thrown. By [@egil](https://github.com/egil) in [#184](https://github.com/egil/bUnit/pull/184). + +## [1.0.0-beta-8] - 2020-07-15 + +Here is beta-8, a small summer vacation release this time. A few needed additions, especially around testing components that use Blazor's authentication and authorization. In addition to this, a lot of documentation has been added to . + +### Added + +List of new features. + +- Authorization fakes added to make it much easier to test components that use authentication and authorization. Learn more in the [Faking Blazor's Authentication and Authorization](https://bunit.egilhansen.com/docs/test-doubles/faking-auth) page. By [@DarthPedro](https://github.com/DarthPedro) in [#151](https://github.com/egil/bUnit/pull/151). + +- Added `MarkupMatches(this string actual ...)` extension methods. Make it easier to compare just the text content from a DON text node with a string, while still getting the benefit of the semantic HTML comparer. + +### Changed + +List of changes in existing functionality. + +- `TestContextBase.Dispose` made virtual to allow inheritor's to override it. By [@SimonCropp](https://github.com/SimonCropp) in [#137](https://github.com/egil/bunit/pull/137). +- **[Breaking change]** Changed naming convention for JSMock feature and moved to new namespace, `Bunit.TestDoubles.JSInterop`. All classes and methods containing `Js` (meaning JavaScript) renamed to `JS` for consistency with Blazor's `IJSRuntime`. By [@yourilima](https://github.com/yourilima) in [#150](https://github.com/egil/bUnit/pull/150) + +## [1.0.0-beta-7] - 2020-05-19## [1.0.0-beta-7] - 2020-05-19 + +There are three big changes in bUnit in this release, as well as a whole host of small new features, improvements to the API, and bug fixes. The three big changes are: + +1. A splitting of the library +2. Discovery of razor base tests, and +3. A strongly typed way to pass parameters to a component under test. + +There are also some breaking changes, which we will cover first. + +**NOTE:** The documentation is next on the TODO list, so please bear with me while I update it to reflect all the recent changes. + +### Breaking changes + +Due to the big restructuring of the library, there are some breaking changes, hopefully for the better. + +#### Razor test changes + +Previously, the `Test` and `Setup` methods on `` and `` did not have any arguments, and the test context they represented when running, was implicitly available in the scope. This has changed with this release, such that all `Test` and `Setup` methods now receive the text context as an argument, and that should be used to call e.g. `GetComponentUnderTest()` on. + +For example, if you have a razor based test that looks like this currently: + +```c# + + + ... + +@code { + void TestSetup() => Services.AddMockJsRuntime(); + + void Test001() + { + var cut = GetComponentUnderTest(); + var fragment = GetFragment(); + } +} +``` + +You have to change it to this: + +```c# + + + +@code { + // Add a Fixture fixture argument to the setup method and use + // the services collection inside the fixture to register dependencies + void TestSetup(Fixture fixture) => fixture.Services.AddMockJsRuntime(); + + // Add a Fixture fixture argument to the test method + void Test001(Fixture fixture) + { + // Use the fixture instance to get the component under test + var cut = fixture.GetComponentUnderTest(); + var fragment = fixture.GetFragment(); + } +} +``` + +It is a little more typing, but it is also a lot more obvious what is going on, e.g. where the component under test or fragment is coming from. + +In addition to this, the `Tests` and `TestsAsync` methods on `` have been deprecated in this release and throws a runtime exception if used. They were not very used and caused confusion about the state of the components under test between the method calls. Now you can only specify either a `Test` or `TestAsync` method per ``. + +#### WaitForRender removed + +The `WaitForRender` method has been removed entirely from the library. Since it would only wait for one render, it had a very specific use case, where as the more general `WaitForAssertion` or `WaitForState` will wait for any number of renders, until the assertion passes, or the state predicate returns true. These make them much better suited to create stable tests. + +With `WaitForRender`, you would pass in an action that would cause a render before attempting your assertion, e.g.: + +```c# +cut.WaitForRender(() => mockForecastService.Task.SetResult(forecasts)); + +Assert.Equal("...", cut.Markup); +``` + +This can now be changed to first call the action that will trigger the render, and then wait for an assertion to pass, using `WaitForAssertion`: + +```c# +mockForecastService.Task.SetResult(forecasts); + +cut.WaitForAssertion(() => Assert.Equal("...", cut.Markup)); +``` + +The two "wait for" methods are also only available through a rendered fragment or rendered component now. + +#### ComponentTestFixture deprecated + +Previously, the recommended method for creating xUnit component test classes was to inherit from `ComponentTestFixture`. Due to the restructuring of the library, this type is now just a `TestContext` with static component parameters factory methods, so it does not add much value anymore. + +The component parameter factory methods are now also available in the more general purpose `ComponentParameterFactory` type, which can be imported into all test classes, not just xUnit ones, using the `import static Bunit.ComponentParameterFactory` method, and then you can change your existing xUnit test classes to inherit from `TestContext` instead of `ComponentTestFixture` to keep the current functionality for xUnit test classes. + +That covers the most important breaking changes. Now lets look at the other big changes. + +### Splitting up the library + +In this release sees bUnit refactored and split up into three different sub libraries. The reasons for doing this are: + +- To make it possible to extract the direct dependency on xUnit and easily add support for NUnit or MSTest +- To make it easier to maintain distinct parts of the library going forward +- To enable future support for other non-web variants of Blazor, e.g. the Blazor Mobile Bindings. + +The three parts of the library is now: + +- **bUnit.core**: The core library only contains code related to the general Blazor component model, i.e. it is not specific to the web version of Blazor. +- **bUnit.web**: The web library, which has a dependency on core, provides all the specific types for rendering and testing Blazor web components. +- **bUnit.xUnit**: The xUnit library, which has a dependency on core, has xUnit specific extensions to bUnit, that enable logging to the test output through the `ILogger` interface in .net core, and an extension to xUnit's test runners, that enable it to discover and run razor based tests defined in `.razor` files. + +To keep things compatible with previous releases, an additional package is available, **bUnit**, which includes all of three libraries. That means existing users should be able to keep their single `` in their projects. + +### Discovery of Razor based tests + +One of the pain points of writing Razor based tests in `.razor` files was that the individual tests was not correctly discovered. That meant that if had multiple tests in a file, you would not see them in Visual Studios Test Explorer individually, you could not run them individually, and error was not reported individually. + +This has changed with the _bUnit.xUnit_ library, that now includes a way for it to discover individual razor tests, currently either a `` or `` inside test components defined in `.razor` files. It also enables you to navigate to the test by double clicking on it in the Test Explorer, and you can run each test individually, and see error reports individually. + +**WARNING:** You still have to wait for the Blazor compiler to translate the `.razor` files into `.cs` files, before the tests show up in the Test Explorer, and the this can trip up the Test Explorer. So while this feature is a big improvement to razor based testing, it is still not perfect, and more works need to be done to refine it. + +### Strongly typed component parameters + +If you prefer writing your tests in C# only, you will be happy to know that there is now a new strongly typed way to pass parameters to components, using a builder. E.g., to render a `ContactInfo` component: + +```c# +var cut = RenderComponent(parameters => parameters + .Add(p => p.Name, "Egil Hansen") + .Add(p => p.Country, "Iceland") +); +``` + +There are a bunch of different `Add` methods available on the builder, that allows you to easily pass in a `EventCallback`, `ChildContent`, or `RenderFragment`. + +The old way using the component parameter factory methods are still available if you prefer that syntax. + +NOTE: The parameter builder API is experimental at this point, and will likely change. + +### NuGet downloads + +The latest version of the library is available on NuGet in various incarnations: + +| Name | Type | NuGet Download Link | +| -------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| bUnit | Library, includes core, web, and xUnit | [![Nuget](https://img.shields.io/nuget/dt/bunit?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit/) | +| bUnit.core | Library, only core | [![Nuget](https://img.shields.io/nuget/dt/bunit.core?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.core/) | +| bUnit.web | Library, web and core | [![Nuget](https://img.shields.io/nuget/dt/bunit.web?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.web/) | +| bUnit.xUnit | Library, xUnit and core | [![Nuget](https://img.shields.io/nuget/dt/bunit.xunit?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.xunit/) | +| bUnit.template | Template, which currently creates an xUnit based bUnit test projects only | [![Nuget](https://img.shields.io/nuget/dt/bunit.template?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.template/) | + +### Contributions + +Thanks to [Martin Stühmer (@samtrion)](https://github.com/samtrion) and [Stef Heyenrath (@StefH)](https://github.com/StefH) for their code contributions in this release, and to [Brad Wilson (@bradwilson)](https://github.com/bradwilson) for his help with enabling xUnit to discover and run Razor based tests. + +Also a big thank to all you who have contributed by raising issues, participated in issues by helping answer questions and providing input on design and technical issues. + +### Added + +- A new event, `OnAfterRender`, has been added to `IRenderedFragmentBase`, which `IRenderedFragment` inherits from. Subscribers will be invoked each time the rendered fragment is re-rendered. Related issue [#118](https://github.com/egil/bunit/issues/118). +- A new property, `RenderCount`, has been added to `IRenderedFragmentBase`, which `IRenderedFragment` inherits from. Its represents the number of times a rendered fragment has been rendered. Related issue [#118](https://github.com/egil/bunit/issues/118). +- A new event, `OnMarkupUpdated`, has been added to `IRenderedFragmentBase`. Subscribers will be notifid each time the rendered fragments markup has been regenerated. Related issue [#118](https://github.com/egil/bunit/issues/118). +- Due to the [concurrency bug discovered](https://github.com/egil/bunit/issues/108), the entire render notification and markup notification system has been changed. +- A new overload `RenderComponent()` and `SetParameterAndRender()`, which takes a `Action>` as input. That allows you to pass parameters to a component under test in a strongly typed way. Thanks to [@StefH](https://github.com/StefH) for the work on this. Related issues: [#79](https://github.com/egil/bunit/issues/79) and [#36](https://github.com/egil/bunit/issues/36). +- The two razor test types, `` and ``, can now be **skipped**. by setting the `Skip="some reason for skipping"` parameter. Note, this requires support from the test runner, which current only includes bUnit.xUnit. Related issue: [#77](https://github.com/egil/bunit/issues/77). +- The two razor test types, `` and ``, can now have a **timeout** specified, by setting the `Timeout="TimeSpan.FromSeconds(2)"` parameter. Note, this requires support from the test runner, which current only includes bUnit.xUnit. +- An `InvokeAsync` method has been added to the `IRenderedFragmentBase` type, which allows invoking of an action in the context of the associated renderer. Related issue: [#82](https://github.com/egil/bunit/issues/82). +- Enabled the "navigate to test" in Test Explorer. Related issue: [#106](https://github.com/egil/bunit/issues/106). +- Enabled xUnit to discover and run Razor-based tests. Thanks to [Brad Wilson (@bradwilson)](https://github.com/bradwilson) for his help with this. Related issue: [#4](https://github.com/egil/bunit/issues/4). + +### Changed + +- Better error description from `MarkupMatches` when two sets of markup are different. +- The `JsRuntimePlannedInvocation` can now has its response to an invocation set both before and after an invocation is received. It can also have a new response set at any time, which will be used for new invocations. Related issue: [#78](https://github.com/egil/bunit/issues/78). +- The `IDiff` assertion helpers like `ShouldHaveChanges` now takes an `IEnumerable` as input to make it easier to call in scenarios where only an enumerable is available. Related issue: [#87](https://github.com/egil/bunit/issues/87). +- `TextContext` now registers all its test dependencies as services in the `Services` collection. This now includes the `HtmlParser` and `HtmlComparer`. Related issue: [#114](https://github.com/egil/bunit/issues/114). + +### Deprecated + +- The `ComponentTestFixture` has been deprecated in this release, since it just inherits from `TestContex` and surface the component parameter factory methods. Going forward, users are encouraged to instead inherit directly from `TestContext` in their xUnit tests classes, and add a `import static Bunit.ComponentParameterFactory` to your test classes, to continue to use the component parameter factory methods. Related issue: [#108](https://github.com/egil/bunit/issues/108). + +### Removed + +- `` tests no longer supports splitting the test method/assertion step into multiple methods through the `Tests` and `TestsAsync` parameters. +- `WaitForRender` has been removed entirely from the library, as the more general purpose `WaitForAssertion` or `WaitForState` covers its use case. +- `WaitForAssertion` or `WaitForState` is no longer available on `ITestContext` types. They are _still_ available on rendered components and rendered fragments. +- `CreateNodes` method has been removed from `ITextContext`. The ability to convert a markup string to a `INodeList` is available through the `HtmlParser` type registered in `ITextContext.Services` service provider. +- `RenderEvents` has been removed from `IRenderedFragment`, and replaced by the `OnMarkupUpdated` and `OnAfterRender` events. Related issue [#118](https://github.com/egil/bunit/issues/118). +- The generic collection assertion methods `ShouldAllBe(this IEnumerable collection, params Action[] elementInspectors)` and `ShouldAllBe(this IEnumerable collection, params Action[] elementInspectors)` have been removed from the library. + +### Fixed + +- A concurrency issue would surface when a component under test caused asynchronous renders that was awaited using the `WaitForRender`, `WaitForState`, or `WaitForAssertion` methods. Related issue [#118](https://github.com/egil/bunit/issues/118). +- `MarkupMatches` and the related semantic markup diffing, didn't correctly ignore the `__internal_stopPropagation_` and `__internal_preventDefault_` added by Blazor to the rendered markup, when users use the `:stopPropagation` and `:preventDefault` modifiers. Thanks to [@samtrion](https://github.com/samtrion) for reporting and solving this. Related issue: [#111](https://github.com/egil/bunit/issues/111). +- `cut.FindComponent()` didn't return the component inside the component under test. It now searches and finds the first child component of the specified type. + +* * * ## [1.0.0-beta-6] - 2020-03-01 + This release includes a **name change from Blazor Components Testing Library to bUnit**. It also brings along two extra helper methods for working with asynchronously rendering components during testing, and a bunch of internal optimizations and tweaks to the code. -*Why change the name?* Naming is hard, and I initial chose a very product-namy name, that quite clearly stated what the library was for. However, the name isn't very searchable, since it just contains generic keywords, plus, bUnit is just much cooler. It also gave me the opportunity to remove my name from all the namespaces and simplify those. +_Why change the name?_ Naming is hard, and I initial chose a very product-namy name, that quite clearly stated what the library was for. However, the name isn't very searchable, since it just contains generic keywords, plus, bUnit is just much cooler. It also gave me the opportunity to remove my name from all the namespaces and simplify those. ### Contributions + Hugh thanks to [Rastislav Novotný (@duracellko)](https://github.com/duracellko)) for his input and review of the `WaitForX` logic added in this release. ### NuGet + The latest version of the library is availble on NuGet: -| | Type | Link | -| ------------- | ----- | ---- | -| [![Nuget](https://img.shields.io/nuget/dt/bunit?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit/) | Library | [https://www.nuget.org/packages/bunit/](https://www.nuget.org/packages/bunit/) | -| [![Nuget](https://img.shields.io/nuget/dt/bunit.template?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.template/) | Template | [https://www.nuget.org/packages/bunit.template/](https://www.nuget.org/packages/bunit.template/) | +| | Type | Link | +| --------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------ | +| [![Nuget](https://img.shields.io/nuget/dt/bunit?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit/) | Library | | +| [![Nuget](https://img.shields.io/nuget/dt/bunit.template?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.template/) | Template | | ### Added -- **`WaitForState(Func statePredicate, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.** - This method will wait (block) until the provided statePredicate returns true, or the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the statePredicate is evaluated. - - You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue. - The following example tests the `DelayedRenderOnClick.razor` component: +- **`WaitForState(Func statePredicate, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.** + This method will wait (block) until the provided statePredicate returns true, or the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the statePredicate is evaluated. + + You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue. + + The following example tests the `DelayedRenderOnClick.razor` component: ```cshtml // DelayedRenderOnClick.razor @@ -34,7 +1321,7 @@ The latest version of the library is availble on NuGet: @code { public int TimesClicked { get; private set; } - + async Task ClickCounter() { await Task.Delay(1); // wait 1 millisecond @@ -43,7 +1330,7 @@ The latest version of the library is availble on NuGet: } ``` - This is a test that uses `WaitForState` to wait until the component under test has a desired state, before the test continues: + This is a test that uses `WaitForState` to wait until the component under test has a desired state, before the test continues: ```csharp [Fact] @@ -51,22 +1338,22 @@ The latest version of the library is availble on NuGet: { // Arrange var cut = RenderComponent(); - + // Act cut.Find("button").Click(); cut.WaitForState(() => cut.Instance.TimesClicked == 1); - + // Assert cut.Find("p").TextContent.ShouldBe("Times Clicked: 1"); } ``` -- **`WaitForAssertion(Action assertion, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.** - This method will wait (block) until the provided assertion method passes, i.e. runs without throwing an assert exception, or until the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the assertion is attempted. +- **`WaitForAssertion(Action assertion, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.** + This method will wait (block) until the provided assertion method passes, i.e. runs without throwing an assert exception, or until the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the assertion is attempted. - You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue. + You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue. - This is a test that tests the `DelayedRenderOnClick.razor` listed above, and that uses `WaitForAssertion` to attempt the assertion each time the component under test renders: + This is a test that tests the `DelayedRenderOnClick.razor` listed above, and that uses `WaitForAssertion` to attempt the assertion each time the component under test renders: ```csharp [Fact] @@ -74,10 +1361,10 @@ The latest version of the library is availble on NuGet: { // Arrange var cut = RenderComponent(); - + // Act cut.Find("button").Click(); - + // Assert cut.WaitForAssertion( () => cut.Find("p").TextContent.ShouldBe("Times Clicked: 1") @@ -85,8 +1372,8 @@ The latest version of the library is availble on NuGet: } ``` -- **Added support for capturing log statements from the renderer and components under test into the test output.** - To enable this, add a constructor to your test classes that takes the `ITestOutputHelper` as input, then in the constructor call `Services.AddXunitLogger` and pass the `ITestOutputHelper` to it, e.g.: +- **Added support for capturing log statements from the renderer and components under test into the test output.** + To enable this, add a constructor to your test classes that takes the `ITestOutputHelper` as input, then in the constructor call `Services.AddXunitLogger` and pass the `ITestOutputHelper` to it, e.g.: ```csharp // ComponentTest.cs @@ -96,13 +1383,13 @@ The latest version of the library is availble on NuGet: { Services.AddXunitLogger(output, minimumLogLevel: LogLevel.Debug); } - + [Fact] public void Test1() ... } ``` - - For Razor and Snapshot tests, the logger can be added almost the same way. The big difference is that it must be added during *Setup*, e.g.: + + For Razor and Snapshot tests, the logger can be added almost the same way. The big difference is that it must be added during _Setup_, e.g.: ```cshtml // RazorComponentTest.razor @@ -111,12 +1398,12 @@ The latest version of the library is availble on NuGet: @code { private ITestOutputHelper _output; - + public RazorComponentTest(ITestOutputHelper output) { _output = output; } - + void Setup() { Services.AddXunitLogger(_output, minimumLogLevel: LogLevel.Debug); @@ -124,16 +1411,16 @@ The latest version of the library is availble on NuGet: } ``` -- **Added simpler `Template` helper method** - To make it easier to test components with `RenderFragment` parameters (template components) in C# based tests, a new `Template(string name, Func markupFactory)` helper methods have been added. It allows you to create a mock template that uses the `markupFactory` to create the rendered markup from the template. +- **Added simpler `Template` helper method** + To make it easier to test components with `RenderFragment` parameters (template components) in C# based tests, a new `Template(string name, Func markupFactory)` helper methods have been added. It allows you to create a mock template that uses the `markupFactory` to create the rendered markup from the template. - This is an example of testing the `SimpleWithTemplate.razor`, which looks like this: + This is an example of testing the `SimpleWithTemplate.razor`, which looks like this: ```cshtml @typeparam T @foreach (var d in Data) { - @Template(d); + @Template(d); } @code { @@ -142,18 +1429,18 @@ The latest version of the library is availble on NuGet: } ``` - And the test code: + And the test code: ```csharp var cut = RenderComponent>( ("Data", new int[] { 1, 2 }), Template("Template", num => $"

{num}

") ); - + cut.MarkupMatches("

1

2

"); ``` - Using the more general `Template` helper methods, you need to write the `RenderTreeBuilder` logic yourself, e.g.: + Using the more general `Template` helper methods, you need to write the `RenderTreeBuilder` logic yourself, e.g.: ```csharp var cut = RenderComponent>( @@ -164,26 +1451,27 @@ The latest version of the library is availble on NuGet: - **Added logging to TestRenderer.** To make it easier to understand the rendering life-cycle during a test, the `TestRenderer` will now log when ever it dispatches an event or renders a component (the log statements can be access by capturing debug logs in the test results, as mentioned above). -- **Added some of the Blazor frameworks end-2-end tests.** To get better test coverage of the many rendering scenarios supported by Blazor, the [ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) tests from the Blazor frameworks test suite has been converted from a Selenium to a bUnit. The testing style is very similar, so few changes was necessary to port the tests. The two test classes are here, if you want to compare: +- **Added some of the Blazor frameworks end-2-end tests.** To get better test coverage of the many rendering scenarios supported by Blazor, the [ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) tests from the Blazor frameworks test suite has been converted from a Selenium to a bUnit. The testing style is very similar, so few changes was necessary to port the tests. The two test classes are here, if you want to compare: - - [bUnit's ComponentRenderingTest.cs](/master/tests/BlazorE2E/ComponentRenderingTest.cs) - - [Blazor's ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) + - [bUnit's ComponentRenderingTest.cs](/main/tests/BlazorE2E/ComponentRenderingTest.cs) + - [Blazor's ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) ### Changed -- **Namespaces is now `Bunit`** - The namespaces have changed from `Egil.RazorComponents.Testing.Library.*` to simply `Bunit` for the library, and `Bunit.Mocking.JSInterop` for the JSInterop mocking support. -- **Auto-refreshing `IElement`s returned from `Find()`** - `IRenderedFragment.Find(string cssSelector)` now returns a `IElement`, which internally will refresh itself, whenever the rendered fragment it was found in, changes. This means you can now search for an element once in your test and assign it to a variable, and then continue to assert against the same instance, even after triggering renders of the component under test. +- **Namespaces is now `Bunit`** + The namespaces have changed from `Egil.RazorComponents.Testing.Library.*` to simply `Bunit` for the library, and `Bunit.Mocking.JSInterop` for the JSInterop mocking support. - For example, instead of having `cut.Find("p")` in multiple places in the same test, you can do `var p = cut.Find("p")` once, and the use the variable `p` all the places you would otherwise have the `Find(...)` statement. +- **Auto-refreshing `IElement`s returned from `Find()`** + `IRenderedFragment.Find(string cssSelector)` now returns a `IElement`, which internally will refresh itself, whenever the rendered fragment it was found in, changes. This means you can now search for an element once in your test and assign it to a variable, and then continue to assert against the same instance, even after triggering renders of the component under test. -- **Refreshable element collection returned from `FindAll`.** - The `FindAll` query method on `IRenderedFragment` now returns a new type, the `IRefreshableElementCollection` type, and the method also takes a second optional argument now, `bool enableAutoRefresh = false`. + For example, instead of having `cut.Find("p")` in multiple places in the same test, you can do `var p = cut.Find("p")` once, and the use the variable `p` all the places you would otherwise have the `Find(...)` statement. - The `IRefreshableElementCollection` is a special collection type that can rerun the query to refresh its the collection of elements that are found by the CSS selector. This can either be done manually by calling the `Refresh()` method, or automatically whenever the rendered fragment renders and has changes, by setting the property `EnableAutoRefresh` to `true` (default set to `false`). +- **Refreshable element collection returned from `FindAll`.** + The `FindAll` query method on `IRenderedFragment` now returns a new type, the `IRefreshableElementCollection` type, and the method also takes a second optional argument now, `bool enableAutoRefresh = false`. - Here are two example tests, that both test the following `ClickAddsLi.razor` component: + The `IRefreshableElementCollection` is a special collection type that can rerun the query to refresh its the collection of elements that are found by the CSS selector. This can either be done manually by calling the `Refresh()` method, or automatically whenever the rendered fragment renders and has changes, by setting the property `EnableAutoRefresh` to `true` (default set to `false`). + + Here are two example tests, that both test the following `ClickAddsLi.razor` component: ```cshtml