From c125792fbeed6dc2a781af92749a5391892ffbbb Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Thu, 26 Mar 2026 20:21:26 +0100 Subject: [PATCH 01/16] Remove automapper - add new implementation --- Directory.Packages.props | 1 - GrandNode.sln | 15 + .../ExportImport/Mapper/BrandProfile.cs | 2 +- .../ExportImport/Mapper/CategoryProfile.cs | 2 +- .../ExportImport/Mapper/CollectionProfile.cs | 2 +- .../ExportImport/Mapper/ProductProfile.cs | 2 +- .../Grand.Infrastructure.csproj | 2 +- .../Mapper/AutoMapperConfig.cs | 2 +- src/Core/Grand.Infrastructure/StartupBase.cs | 4 +- src/Core/Grand.Mapping/Grand.Mapping.csproj | 3 + src/Core/Grand.Mapping/GrandMapper.cs | 22 ++ src/Core/Grand.Mapping/IMapper.cs | 7 + .../IMapperConfigurationExpression.cs | 7 + src/Core/Grand.Mapping/IMappingExpression.cs | 14 + .../IMemberConfigurationExpression.cs | 10 + .../Internal/IMappingConfiguration.cs | 7 + .../MapperConfigurationExpressionImpl.cs | 14 + .../Grand.Mapping/Internal/MappingCompiler.cs | 256 ++++++++++++++++++ .../Internal/MappingConfiguration.cs | 14 + .../Internal/MappingExpressionImpl.cs | 53 ++++ .../Grand.Mapping/Internal/MemberConfig.cs | 13 + .../Internal/ParameterReplacer.cs | 21 ++ src/Core/Grand.Mapping/MapperConfiguration.cs | 21 ++ src/Core/Grand.Mapping/Profile.cs | 17 ++ .../Mapper/Profiles/AddressProfile.cs | 2 +- .../Mapper/Profiles/BrandProfile.cs | 2 +- .../Mapper/Profiles/CategoryProfile.cs | 2 +- .../Mapper/Profiles/CollectionProfile.cs | 2 +- .../Mapper/Profiles/CustomerGroupProfile.cs | 2 +- .../Mapper/Profiles/CustomerProfile.cs | 2 +- .../Mapper/Profiles/PictureProfile.cs | 2 +- .../ProductAttributeMappingProfile.cs | 2 +- .../Profiles/ProductAttributeProfile.cs | 2 +- .../Mapper/Profiles/ProductProfile.cs | 2 +- .../Profiles/SpecificationAttributeProfile.cs | 2 +- .../Mapper/Profiles/TierPriceProfile.cs | 2 +- .../MapperConfiguration.cs | 2 +- .../Mapper/SliderMapperConfiguration.cs | 2 +- .../BrandImportDataObjectTests.cs | 2 +- .../CategoryImportDataObjectTests.cs | 2 +- .../CollectionImportDataObjectTests.cs | 2 +- .../ProductImportDataObjectTests.cs | 2 +- .../MenuViewModelServiceTests.cs | 2 +- .../Mapper/AddressAttributeProfile.cs | 2 +- .../Mapper/AddressAttributeValueProfile.cs | 2 +- .../Mapper/AddressProfile.cs | 2 +- .../Mapper/AddressSettingsProfile.cs | 2 +- .../Mapper/AdminSearchSettingsProfile.cs | 2 +- .../Mapper/BlogCategoryProfile.cs | 2 +- .../Mapper/BlogPostProfile.cs | 2 +- .../Mapper/BlogSettingsProfile.cs | 2 +- .../Mapper/BrandLayoutProfile.cs | 2 +- .../Mapper/BrandProfile.cs | 2 +- .../Mapper/CampaignProfile.cs | 2 +- .../Mapper/CaptchaSettingsProfile.cs | 2 +- .../Mapper/CatalogSettingsProfile.cs | 2 +- .../Mapper/CategoryLayoutProfile.cs | 2 +- .../Mapper/CategoryProfile.cs | 2 +- .../Mapper/CheckoutAttributeProfile.cs | 2 +- .../Mapper/CollectionLayoutProfile.cs | 2 +- .../Mapper/CollectionProfile.cs | 2 +- .../Mapper/CommonSettingsProfile.cs | 2 +- .../Mapper/ContactAttributeProfile.cs | 2 +- .../Mapper/ContactUsProfile.cs | 2 +- .../Mapper/CountryProfile.cs | 2 +- .../Mapper/CourseLessonProfile.cs | 2 +- .../Mapper/CourseLevelProfile.cs | 2 +- .../Mapper/CourseProfile.cs | 2 +- .../Mapper/CourseSubjectProfile.cs | 2 +- .../Mapper/CurrencyProfile.cs | 2 +- .../Mapper/CustomerAttributeProfile.cs | 2 +- .../Mapper/CustomerAttributeValueProfile.cs | 2 +- .../Mapper/CustomerGroupProfile.cs | 2 +- .../Mapper/CustomerSettingsProfile.cs | 2 +- .../Mapper/CustomerTagProfile.cs | 2 +- .../Mapper/DeliveryDateProfile.cs | 2 +- .../Mapper/DiscountProfile.cs | 2 +- .../Mapper/DocumentProfile.cs | 2 +- .../Mapper/DocumentTypeProfile.cs | 2 +- .../Mapper/EmailAccountProfile.cs | 2 +- .../ExternalAuthenticationMethodProfile.cs | 2 +- .../Mapper/GiftVoucherProfile.cs | 2 +- .../Mapper/KnowledgebaseCategoryProfile.cs | 2 +- .../Mapper/KnowledgebaseSettingsProfile.cs | 2 +- .../Mapper/LanguageProfile.cs | 2 +- .../Mapper/LoyaltyPointsSettingsProfile.cs | 2 +- .../Mapper/MeasureDimensionProfile.cs | 2 +- .../Mapper/MeasureUnitProfile.cs | 2 +- .../Mapper/MeasureWeightProfile.cs | 2 +- .../Mapper/MediaSettingsProfile.cs | 2 +- .../Mapper/MenuItemSettingsProfile.cs | 2 +- .../Mapper/MenuProfile.cs | 2 +- .../Mapper/MerchandiseReturnActionProfile.cs | 2 +- .../Mapper/MerchandiseReturnReasonProfile.cs | 2 +- .../Mapper/MessageTemplateProfile.cs | 2 +- .../Mapper/NewsItemProfile.cs | 2 +- .../Mapper/NewsLetterSubscriptionProfile.cs | 2 +- .../Mapper/NewsSettingsProfile.cs | 2 +- .../Mapper/NewsletterCategoryProfile.cs | 2 +- .../Mapper/OrderSettingsProfile.cs | 2 +- .../Mapper/OrderStatusProfile.cs | 2 +- .../Mapper/PageLayoutProfile.cs | 2 +- .../Mapper/PageProfile.cs | 2 +- .../Mapper/PaymentMethodProfile.cs | 2 +- .../Mapper/PaymentSettingsProfile.cs | 2 +- .../Mapper/PdfSettingsProfile.cs | 2 +- .../Mapper/PermissionProfile.cs | 2 +- .../Mapper/PickupPointProfile.cs | 2 +- .../Mapper/PluginDescriptorProfile.cs | 2 +- .../PredefinedProductAttributeValueProfile.cs | 2 +- .../ProductAttributeCombinationProfile.cs | 2 +- .../Mapper/ProductAttributeMappingProfile.cs | 2 +- .../Mapper/ProductAttributeProfile.cs | 2 +- .../Mapper/ProductProfile.cs | 2 +- .../Mapper/ProductReviewProfile.cs | 2 +- .../Mapper/ProductSpecificationProfile.cs | 2 +- .../Mapper/ProductlayoutProfile.cs | 2 +- .../PushNotificationsSettingsProfile.cs | 2 +- .../Mapper/QueuedEmailProfile.cs | 2 +- .../Mapper/SalesEmployeeProfile.cs | 2 +- .../Mapper/ScheduleTaskProfile.cs | 2 +- .../Mapper/SeoSettingsProfile.cs | 2 +- .../Mapper/ShippingMethodProfile.cs | 2 +- .../ShippingRateComputationMethodProfile.cs | 2 +- .../Mapper/ShippingSettingsProfile.cs | 2 +- .../Mapper/ShoppingCartSettingsProfile.cs | 2 +- .../Mapper/SpecificationAttributeProfile.cs | 2 +- .../Mapper/StateProvinceProfile.cs | 2 +- .../Mapper/StoreInformationSettingsProfile.cs | 2 +- .../Mapper/StoreProfile.cs | 2 +- .../Mapper/TaxCategoryProfile.cs | 2 +- .../Mapper/TaxProviderProfile.cs | 2 +- .../Mapper/TaxSettingsProfile.cs | 2 +- .../Mapper/TierPriceProfile.cs | 2 +- .../Mapper/UserApiProfile.cs | 2 +- .../Mapper/VendorProfile.cs | 2 +- .../Mapper/VendorSettingsProfile.cs | 2 +- .../Mapper/WarehouseProfile.cs | 2 +- .../Mapper/WidgetPluginProfile.cs | 2 +- .../Grand.Web.Vendor/Mapper/AddressProfile.cs | 2 +- .../Grand.Web.Vendor/Mapper/ProductProfile.cs | 2 +- .../Grand.Web.Vendor/Mapper/VendorProfile.cs | 2 +- 142 files changed, 620 insertions(+), 127 deletions(-) create mode 100644 src/Core/Grand.Mapping/Grand.Mapping.csproj create mode 100644 src/Core/Grand.Mapping/GrandMapper.cs create mode 100644 src/Core/Grand.Mapping/IMapper.cs create mode 100644 src/Core/Grand.Mapping/IMapperConfigurationExpression.cs create mode 100644 src/Core/Grand.Mapping/IMappingExpression.cs create mode 100644 src/Core/Grand.Mapping/IMemberConfigurationExpression.cs create mode 100644 src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs create mode 100644 src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs create mode 100644 src/Core/Grand.Mapping/Internal/MappingCompiler.cs create mode 100644 src/Core/Grand.Mapping/Internal/MappingConfiguration.cs create mode 100644 src/Core/Grand.Mapping/Internal/MappingExpressionImpl.cs create mode 100644 src/Core/Grand.Mapping/Internal/MemberConfig.cs create mode 100644 src/Core/Grand.Mapping/Internal/ParameterReplacer.cs create mode 100644 src/Core/Grand.Mapping/MapperConfiguration.cs create mode 100644 src/Core/Grand.Mapping/Profile.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 5f787c5f7..d5183dc12 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -30,7 +30,6 @@ - diff --git a/GrandNode.sln b/GrandNode.sln index c6bd60b9f..d754ecd98 100644 --- a/GrandNode.sln +++ b/GrandNode.sln @@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grand.Web.Common", "src\Web EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grand.Infrastructure", "src\Core\Grand.Infrastructure\Grand.Infrastructure.csproj", "{34A46D97-5996-46B3-BDCA-631EDAA0E210}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grand.Mapping", "src\Core\Grand.Mapping\Grand.Mapping.csproj", "{D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{F6F48227-B3C5-4A51-B33F-FF9AD96352DB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authentication.Facebook", "src\Plugins\Authentication.Facebook\Authentication.Facebook.csproj", "{BA4579AF-9C20-477A-891B-926968B11C68}" @@ -323,6 +325,18 @@ Global {34A46D97-5996-46B3-BDCA-631EDAA0E210}.Release|x64.Build.0 = Release|Any CPU {34A46D97-5996-46B3-BDCA-631EDAA0E210}.Release|x86.ActiveCfg = Release|Any CPU {34A46D97-5996-46B3-BDCA-631EDAA0E210}.Release|x86.Build.0 = Release|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Debug|x64.ActiveCfg = Debug|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Debug|x64.Build.0 = Debug|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Debug|x86.ActiveCfg = Debug|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Debug|x86.Build.0 = Debug|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Release|Any CPU.Build.0 = Release|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Release|x64.ActiveCfg = Release|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Release|x64.Build.0 = Release|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Release|x86.ActiveCfg = Release|Any CPU + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9}.Release|x86.Build.0 = Release|Any CPU {BA4579AF-9C20-477A-891B-926968B11C68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BA4579AF-9C20-477A-891B-926968B11C68}.Debug|Any CPU.Build.0 = Debug|Any CPU {BA4579AF-9C20-477A-891B-926968B11C68}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -882,6 +896,7 @@ Global {D2E8AC42-2751-4AF9-87E4-54A8B2034360} = {6EAC4D2C-4A86-4C9F-8427-CB374F69F08C} {1A6A0104-F610-4DD3-88BE-C4029034E5DA} = {BF4543A8-0731-4FDD-BB6B-0ADB9561F981} {34A46D97-5996-46B3-BDCA-631EDAA0E210} = {FA350BD6-C29D-40D9-BA47-FE5FBDFC84A0} + {D4E5F6A7-B8C9-4D0E-A236-B4C5D6E7F8A9} = {FA350BD6-C29D-40D9-BA47-FE5FBDFC84A0} {BA4579AF-9C20-477A-891B-926968B11C68} = {F6F48227-B3C5-4A51-B33F-FF9AD96352DB} {6F24BF9C-EA4E-42AB-A9A8-435CE69362B8} = {F6F48227-B3C5-4A51-B33F-FF9AD96352DB} {64775F93-5A04-495F-97C6-BDB3835B18F6} = {F6F48227-B3C5-4A51-B33F-FF9AD96352DB} diff --git a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/BrandProfile.cs b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/BrandProfile.cs index e2dbaca30..090937991 100644 --- a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/BrandProfile.cs +++ b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/BrandProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Dto; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CategoryProfile.cs b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CategoryProfile.cs index 383bfe4a1..3659da20c 100644 --- a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CategoryProfile.cs +++ b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Dto; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CollectionProfile.cs b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CollectionProfile.cs index 0e72900d6..21356d7cf 100644 --- a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CollectionProfile.cs +++ b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/CollectionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Dto; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/ProductProfile.cs b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/ProductProfile.cs index d6fe0798c..dd5a3a775 100644 --- a/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/ProductProfile.cs +++ b/src/Business/Grand.Business.Catalog/Services/ExportImport/Mapper/ProductProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Dto; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Core/Grand.Infrastructure/Grand.Infrastructure.csproj b/src/Core/Grand.Infrastructure/Grand.Infrastructure.csproj index 806f5ef2f..c2d4600b1 100644 --- a/src/Core/Grand.Infrastructure/Grand.Infrastructure.csproj +++ b/src/Core/Grand.Infrastructure/Grand.Infrastructure.csproj @@ -8,7 +8,6 @@ - @@ -20,5 +19,6 @@ + \ No newline at end of file diff --git a/src/Core/Grand.Infrastructure/Mapper/AutoMapperConfig.cs b/src/Core/Grand.Infrastructure/Mapper/AutoMapperConfig.cs index 9d32a43de..48d56acef 100644 --- a/src/Core/Grand.Infrastructure/Mapper/AutoMapperConfig.cs +++ b/src/Core/Grand.Infrastructure/Mapper/AutoMapperConfig.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; namespace Grand.Infrastructure.Mapper; diff --git a/src/Core/Grand.Infrastructure/StartupBase.cs b/src/Core/Grand.Infrastructure/StartupBase.cs index 33f1c35d8..4eb7c3344 100644 --- a/src/Core/Grand.Infrastructure/StartupBase.cs +++ b/src/Core/Grand.Infrastructure/StartupBase.cs @@ -1,5 +1,5 @@ -using AutoMapper; -using Grand.Data; +using Grand.Data; +using Grand.Mapping; using Grand.Infrastructure.Configuration; using Grand.Infrastructure.Extensions; using Grand.Infrastructure.Mapper; diff --git a/src/Core/Grand.Mapping/Grand.Mapping.csproj b/src/Core/Grand.Mapping/Grand.Mapping.csproj new file mode 100644 index 000000000..b9b0d198f --- /dev/null +++ b/src/Core/Grand.Mapping/Grand.Mapping.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Core/Grand.Mapping/GrandMapper.cs b/src/Core/Grand.Mapping/GrandMapper.cs new file mode 100644 index 000000000..0726b8030 --- /dev/null +++ b/src/Core/Grand.Mapping/GrandMapper.cs @@ -0,0 +1,22 @@ +namespace Grand.Mapping; + +internal sealed class GrandMapper : IMapper +{ + private readonly Dictionary<(Type, Type), Delegate> _mappings; + + internal GrandMapper(Dictionary<(Type, Type), Delegate> mappings) => _mappings = mappings; + + public TDest Map(TSource source) + { + var dest = (TDest)Activator.CreateInstance(typeof(TDest))!; + return Map(source, dest); + } + + public TDest Map(TSource source, TDest destination) + { + if (source is null || destination is null) return destination!; + if (_mappings.TryGetValue((typeof(TSource), typeof(TDest)), out var del)) + ((Action)del)(source, destination); + return destination; + } +} diff --git a/src/Core/Grand.Mapping/IMapper.cs b/src/Core/Grand.Mapping/IMapper.cs new file mode 100644 index 000000000..6b3131dfe --- /dev/null +++ b/src/Core/Grand.Mapping/IMapper.cs @@ -0,0 +1,7 @@ +namespace Grand.Mapping; + +public interface IMapper +{ + TDest Map(TSource source); + TDest Map(TSource source, TDest destination); +} diff --git a/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs b/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs new file mode 100644 index 000000000..3765221c3 --- /dev/null +++ b/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs @@ -0,0 +1,7 @@ +namespace Grand.Mapping; + +public interface IMapperConfigurationExpression +{ + void AddProfile(Profile profile); + void AddProfile(Type profileType); +} diff --git a/src/Core/Grand.Mapping/IMappingExpression.cs b/src/Core/Grand.Mapping/IMappingExpression.cs new file mode 100644 index 000000000..8ee91b5a2 --- /dev/null +++ b/src/Core/Grand.Mapping/IMappingExpression.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; + +namespace Grand.Mapping; + +public interface IMappingExpression +{ + IMappingExpression ForMember( + Expression> destMember, + Action> opts); + + IMappingExpression ForPath( + Expression> destPath, + Action> opts); +} diff --git a/src/Core/Grand.Mapping/IMemberConfigurationExpression.cs b/src/Core/Grand.Mapping/IMemberConfigurationExpression.cs new file mode 100644 index 000000000..2e73fc566 --- /dev/null +++ b/src/Core/Grand.Mapping/IMemberConfigurationExpression.cs @@ -0,0 +1,10 @@ +using System.Linq.Expressions; + +namespace Grand.Mapping; + +public interface IMemberConfigurationExpression +{ + void Ignore(); + void MapFrom(Expression> mapExpression); + void Condition(Expression> condition); +} diff --git a/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs b/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs new file mode 100644 index 000000000..ba144628d --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs @@ -0,0 +1,7 @@ +namespace Grand.Mapping.Internal; + +internal interface IMappingConfiguration +{ + (Type Source, Type Dest) GetTypes(); + Delegate CompileDelegate(); +} diff --git a/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs b/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs new file mode 100644 index 000000000..64e8e6e90 --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs @@ -0,0 +1,14 @@ +namespace Grand.Mapping.Internal; + +internal sealed class MapperConfigurationExpressionImpl : IMapperConfigurationExpression +{ + private readonly List _configurations = new(); + + public void AddProfile(Profile profile) + => _configurations.AddRange(profile.GetConfigurations()); + + public void AddProfile(Type profileType) + => AddProfile((Profile)Activator.CreateInstance(profileType)!); + + internal IEnumerable GetConfigurations() => _configurations; +} diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs new file mode 100644 index 000000000..2503c2b5e --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -0,0 +1,256 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace Grand.Mapping.Internal; + +internal static class MappingCompiler +{ + public static Action Compile(List configs) + { + var srcParam = Expression.Parameter(typeof(TSource), "src"); + var dstParam = Expression.Parameter(typeof(TDest), "dst"); + var statements = new List(); + + // Index non-path configs by member name (last ForMember wins for duplicates) + var directConfigs = new Dictionary(StringComparer.Ordinal); + foreach (var c in configs.Where(c => !c.IsPath)) + directConfigs[c.MemberName] = c; + + // Process all writable destination properties + var destProps = typeof(TDest) + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.CanWrite); + + foreach (var destProp in destProps) + { + if (directConfigs.TryGetValue(destProp.Name, out var config)) + { + if (config.IsIgnored) continue; + + Expression? valueExpr; + + if (config.MapFromExpression != null) + { + // Custom mapping: inline the MapFrom lambda body with substituted src parameter + valueExpr = ParameterReplacer.Replace( + config.MapFromExpression.Body, + config.MapFromExpression.Parameters[0], + srcParam); + } + else + { + // Condition-only: auto-map from same-named source property + var srcProp = typeof(TSource).GetProperty(destProp.Name, + BindingFlags.Public | BindingFlags.Instance); + if (srcProp == null || !srcProp.CanRead) continue; + valueExpr = Expression.Property(srcParam, srcProp); + } + + valueExpr = TryConvert(valueExpr, destProp.PropertyType); + if (valueExpr == null) continue; + + var dstAccess = Expression.Property(dstParam, destProp); + var assignment = Expression.Assign(dstAccess, valueExpr); + statements.Add(WrapInCondition(assignment, config.ConditionExpression, srcParam)); + } + else + { + // Auto-mapping: same-named property in source with compatible type + var srcProp = typeof(TSource).GetProperty(destProp.Name, + BindingFlags.Public | BindingFlags.Instance); + if (srcProp == null || !srcProp.CanRead) continue; + + var valueExpr = TryConvert( + Expression.Property(srcParam, srcProp), + destProp.PropertyType); + if (valueExpr == null) continue; + + var dstAccess = Expression.Property(dstParam, destProp); + statements.Add(Expression.Assign(dstAccess, valueExpr)); + } + } + + // Process ForPath configs + foreach (var config in configs.Where(c => c.IsPath && !c.IsIgnored)) + { + if (config.DestinationPathExpression == null || config.MapFromExpression == null) continue; + + var destAccess = ParameterReplacer.Replace( + config.DestinationPathExpression.Body, + config.DestinationPathExpression.Parameters[0], + dstParam); + + if (destAccess is not MemberExpression memberAccess) continue; + + var valueExpr = ParameterReplacer.Replace( + config.MapFromExpression.Body, + config.MapFromExpression.Parameters[0], + srcParam); + + valueExpr = TryConvert(valueExpr, memberAccess.Type) ?? valueExpr; + + var assignment = Expression.Assign(destAccess, valueExpr); + statements.Add(WrapInCondition(assignment, config.ConditionExpression, srcParam)); + } + + var body = statements.Count > 0 + ? (Expression)Expression.Block(statements) + : Expression.Empty(); + + return Expression.Lambda>(body, srcParam, dstParam).Compile(); + } + + private static Expression? TryConvert(Expression expr, Type targetType) + { + if (expr.Type == targetType) return expr; + + // T → Nullable: requires explicit Convert node for Expression.Assign. + if (targetType.IsGenericType + && targetType.GetGenericTypeDefinition() == typeof(Nullable<>) + && targetType.GetGenericArguments()[0] == expr.Type) + { + return Expression.Convert(expr, targetType); + } + + // Nullable → T: same reason. + var underlyingTarget = Nullable.GetUnderlyingType(targetType); + if (underlyingTarget != null && underlyingTarget == expr.Type) + return Expression.Convert(expr, targetType); + + // Collection coercions: IEnumerable/IList/etc. → T[] or List. + // AutoMapper handles these implicitly; we do the same via Enumerable.ToArray/ToList. + var collectionExpr = TryBuildCollectionConversion(expr, targetType); + if (collectionExpr != null) return collectionExpr; + + // Direct upcast — no Convert node needed. + if (targetType.IsAssignableFrom(expr.Type)) return expr; + + // Numeric / enum value-type conversions. + if (IsNumericOrEnum(expr.Type) && IsNumericOrEnum(targetType)) + { + try { return Expression.Convert(expr, targetType); } + catch { return null; } + } + + // User-defined conversion operator (Method != null). + // Do NOT fall back to a raw reference cast (Method == null) for incompatible + // reference types such as List → T[] — it compiles but throws InvalidCastException. + try + { + var conv = Expression.Convert(expr, targetType); + return conv.Method != null ? conv : null; + } + catch + { + return null; + } + } + + /// + /// Builds a null-safe collection conversion expression: + /// IEnumerable<T> → T[] via src == null ? null : Enumerable.ToArray(src) + /// IEnumerable<T> → List<T> via src == null ? null : Enumerable.ToList(src) + /// Returns null when the types are not a supported collection pair. + /// + private static Expression? TryBuildCollectionConversion(Expression expr, Type targetType) + { + // Determine source element type from IEnumerable + var srcElementType = GetEnumerableElementType(expr.Type); + if (srcElementType == null) return null; + + // Target: T[] + if (targetType.IsArray && targetType.GetArrayRank() == 1) + { + var destElementType = targetType.GetElementType()!; + if (srcElementType != destElementType) return null; + + var toArray = typeof(Enumerable) + .GetMethod(nameof(Enumerable.ToArray))! + .MakeGenericMethod(destElementType); + + return BuildNullSafeCall(expr, targetType, toArray); + } + + // Target: List + if (targetType.IsGenericType + && targetType.GetGenericTypeDefinition() == typeof(List<>)) + { + var destElementType = targetType.GetGenericArguments()[0]; + if (srcElementType != destElementType) return null; + + var toList = typeof(Enumerable) + .GetMethod(nameof(Enumerable.ToList))! + .MakeGenericMethod(destElementType); + + return BuildNullSafeCall(expr, targetType, toList); + } + + return null; + } + + private static Type? GetEnumerableElementType(Type type) + { + if (type == typeof(string)) return null; // string is IEnumerable, skip + if (type.IsArray) return type.GetElementType(); + + // Direct generic: IEnumerable, IList, ICollection, etc. + if (type.IsGenericType) + { + var def = type.GetGenericTypeDefinition(); + if (def == typeof(IEnumerable<>) || def == typeof(IList<>) + || def == typeof(ICollection<>) || def == typeof(IReadOnlyList<>) + || def == typeof(IReadOnlyCollection<>) || def == typeof(List<>)) + { + return type.GetGenericArguments()[0]; + } + } + + // Interface implemented: find IEnumerable + foreach (var iface in type.GetInterfaces()) + { + if (iface.IsGenericType + && iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + return iface.GetGenericArguments()[0]; + } + } + + return null; + } + + // Generates: src == null ? (T[])null : Enumerable.ToArray(src) + private static Expression BuildNullSafeCall(Expression expr, Type targetType, MethodInfo method) + { + if (expr.Type.IsValueType) + return Expression.Call(method, expr); + + var iEnumType = method.GetParameters()[0].ParameterType; + var srcArg = expr.Type == iEnumType ? expr : Expression.Convert(expr, iEnumType); + + return Expression.Condition( + Expression.ReferenceEqual(expr, Expression.Constant(null, expr.Type)), + Expression.Constant(null, targetType), + Expression.Call(method, srcArg)); + } + + private static bool IsNumericOrEnum(Type t) + { + t = Nullable.GetUnderlyingType(t) ?? t; + return t.IsEnum || t == typeof(byte) || t == typeof(sbyte) + || t == typeof(short) || t == typeof(ushort) + || t == typeof(int) || t == typeof(uint) + || t == typeof(long) || t == typeof(ulong) + || t == typeof(float) || t == typeof(double) + || t == typeof(decimal); + } + + private static Expression WrapInCondition( + Expression body, + LambdaExpression? condition, + ParameterExpression srcParam) + { + if (condition == null) return body; + var condBody = ParameterReplacer.Replace(condition.Body, condition.Parameters[0], srcParam); + return Expression.IfThen(condBody, body); + } +} diff --git a/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs b/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs new file mode 100644 index 000000000..645c23a3f --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs @@ -0,0 +1,14 @@ +namespace Grand.Mapping.Internal; + +internal sealed class MappingConfiguration : IMappingConfiguration +{ + private readonly MappingExpressionImpl _expression; + + public MappingConfiguration(MappingExpressionImpl expression) + => _expression = expression; + + public (Type Source, Type Dest) GetTypes() => (typeof(TSource), typeof(TDest)); + + public Delegate CompileDelegate() + => MappingCompiler.Compile(_expression.MemberConfigs); +} diff --git a/src/Core/Grand.Mapping/Internal/MappingExpressionImpl.cs b/src/Core/Grand.Mapping/Internal/MappingExpressionImpl.cs new file mode 100644 index 000000000..0a6457526 --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/MappingExpressionImpl.cs @@ -0,0 +1,53 @@ +using System.Linq.Expressions; + +namespace Grand.Mapping.Internal; + +internal sealed class MappingExpressionImpl : IMappingExpression +{ + internal readonly List MemberConfigs = new(); + + public IMappingExpression ForMember( + Expression> destMember, + Action> opts) + { + var body = destMember.Body; + // Unwrap UnaryExpression conversion if present (e.g., boxing) + if (body is UnaryExpression unary) body = unary.Operand; + var config = new MemberConfig { + MemberName = ((MemberExpression)body).Member.Name, + IsPath = false + }; + opts(new MemberConfigExpressionImpl(config)); + MemberConfigs.Add(config); + return this; + } + + public IMappingExpression ForPath( + Expression> destPath, + Action> opts) + { + var config = new MemberConfig { + DestinationPathExpression = destPath, + IsPath = true + }; + opts(new MemberConfigExpressionImpl(config)); + MemberConfigs.Add(config); + return this; + } +} + +internal sealed class MemberConfigExpressionImpl + : IMemberConfigurationExpression +{ + private readonly MemberConfig _config; + + public MemberConfigExpressionImpl(MemberConfig config) => _config = config; + + public void Ignore() => _config.IsIgnored = true; + + public void MapFrom(Expression> mapExpression) + => _config.MapFromExpression = mapExpression; + + public void Condition(Expression> condition) + => _config.ConditionExpression = condition; +} diff --git a/src/Core/Grand.Mapping/Internal/MemberConfig.cs b/src/Core/Grand.Mapping/Internal/MemberConfig.cs new file mode 100644 index 000000000..f981b7b1a --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/MemberConfig.cs @@ -0,0 +1,13 @@ +using System.Linq.Expressions; + +namespace Grand.Mapping.Internal; + +internal sealed class MemberConfig +{ + public bool IsIgnored { get; set; } + public LambdaExpression? MapFromExpression { get; set; } + public LambdaExpression? ConditionExpression { get; set; } + public string MemberName { get; set; } = ""; + public LambdaExpression? DestinationPathExpression { get; set; } + public bool IsPath { get; set; } +} diff --git a/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs b/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs new file mode 100644 index 000000000..9c7b3694d --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs @@ -0,0 +1,21 @@ +using System.Linq.Expressions; + +namespace Grand.Mapping.Internal; + +internal sealed class ParameterReplacer : ExpressionVisitor +{ + private readonly ParameterExpression _oldParam; + private readonly Expression _newExpr; + + private ParameterReplacer(ParameterExpression oldParam, Expression newExpr) + { + _oldParam = oldParam; + _newExpr = newExpr; + } + + public static Expression Replace(Expression body, ParameterExpression oldParam, Expression newExpr) + => new ParameterReplacer(oldParam, newExpr).Visit(body)!; + + protected override Expression VisitParameter(ParameterExpression node) + => node == _oldParam ? _newExpr : base.VisitParameter(node); +} diff --git a/src/Core/Grand.Mapping/MapperConfiguration.cs b/src/Core/Grand.Mapping/MapperConfiguration.cs new file mode 100644 index 000000000..b2e177e03 --- /dev/null +++ b/src/Core/Grand.Mapping/MapperConfiguration.cs @@ -0,0 +1,21 @@ +using Grand.Mapping.Internal; + +namespace Grand.Mapping; + +public sealed class MapperConfiguration +{ + private readonly Dictionary<(Type, Type), Delegate> _mappings = new(); + + public MapperConfiguration(Action configure) + { + var expr = new MapperConfigurationExpressionImpl(); + configure(expr); + foreach (var config in expr.GetConfigurations()) + { + var key = config.GetTypes(); + _mappings[key] = config.CompileDelegate(); + } + } + + public IMapper CreateMapper() => new GrandMapper(_mappings); +} diff --git a/src/Core/Grand.Mapping/Profile.cs b/src/Core/Grand.Mapping/Profile.cs new file mode 100644 index 000000000..775e816c0 --- /dev/null +++ b/src/Core/Grand.Mapping/Profile.cs @@ -0,0 +1,17 @@ +using Grand.Mapping.Internal; + +namespace Grand.Mapping; + +public abstract class Profile +{ + private readonly List _configurations = new(); + + protected IMappingExpression CreateMap() + { + var expr = new MappingExpressionImpl(); + _configurations.Add(new MappingConfiguration(expr)); + return expr; + } + + internal IEnumerable GetConfigurations() => _configurations; +} diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/AddressProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/AddressProfile.cs index 47daeddd7..47df0ac81 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/AddressProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/AddressProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Customers; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/BrandProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/BrandProfile.cs index 7f97ae451..43618f375 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/BrandProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/BrandProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CategoryProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CategoryProfile.cs index 81134dc3f..bc346eeec 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CategoryProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CollectionProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CollectionProfile.cs index 52d6c3e9e..73f5ac508 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CollectionProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CollectionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerGroupProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerGroupProfile.cs index d00fc1ba8..b5bc43bf0 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerGroupProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerGroupProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Customers; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerProfile.cs index f55ded707..4bad0aef5 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/CustomerProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Customers; using Grand.Domain.Common; using Grand.Domain.Customers; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/PictureProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/PictureProfile.cs index 6a2d98c25..4715e3106 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/PictureProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/PictureProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Common; using Grand.Domain.Media; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeMappingProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeMappingProfile.cs index 155d10a92..ddfda7512 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeMappingProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeMappingProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeProfile.cs index 972fb270a..40bbdee3a 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductProfile.cs index 3a108eb0b..bd4da4b88 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/ProductProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/SpecificationAttributeProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/SpecificationAttributeProfile.cs index a667583a1..a9a023292 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/SpecificationAttributeProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/SpecificationAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/TierPriceProfile.cs b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/TierPriceProfile.cs index 97b96fd01..39df1dd7c 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/TierPriceProfile.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Mapper/Profiles/TierPriceProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Module.Api.DTOs.Catalog; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Plugins/Shipping.ShippingPoint/MapperConfiguration.cs b/src/Plugins/Shipping.ShippingPoint/MapperConfiguration.cs index bb454edf8..deb429271 100644 --- a/src/Plugins/Shipping.ShippingPoint/MapperConfiguration.cs +++ b/src/Plugins/Shipping.ShippingPoint/MapperConfiguration.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Infrastructure.Mapper; using Shipping.ShippingPoint.Domain; using Shipping.ShippingPoint.Models; diff --git a/src/Plugins/Widgets.Slider/Infrastructure/Mapper/SliderMapperConfiguration.cs b/src/Plugins/Widgets.Slider/Infrastructure/Mapper/SliderMapperConfiguration.cs index 26e914544..f723927da 100644 --- a/src/Plugins/Widgets.Slider/Infrastructure/Mapper/SliderMapperConfiguration.cs +++ b/src/Plugins/Widgets.Slider/Infrastructure/Mapper/SliderMapperConfiguration.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Infrastructure.Mapper; using Widgets.Slider.Domain; using Widgets.Slider.Models; diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs index bf19ad0ca..973840c94 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Catalog.Services.Brands; using Grand.Business.Catalog.Services.ExportImport; using Grand.Business.Common.Services.Seo; diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs index 7efa3521c..1c2daa58f 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Catalog.Services.Categories; using Grand.Business.Catalog.Services.ExportImport; using Grand.Business.Common.Services.Security; diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs index f387c9d7f..8422129cd 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Catalog.Services.Collections; using Grand.Business.Catalog.Services.ExportImport; using Grand.Business.Common.Services.Security; diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs index 0e4035aaa..6a704b10c 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Catalog.Services.ExportImport; using Grand.Business.Catalog.Services.Products; using Grand.Business.Common.Services.Security; diff --git a/src/Tests/Grand.Web.Admin.Tests/MenuViewModelServiceTests.cs b/src/Tests/Grand.Web.Admin.Tests/MenuViewModelServiceTests.cs index f8eaafc0d..28344ee67 100644 --- a/src/Tests/Grand.Web.Admin.Tests/MenuViewModelServiceTests.cs +++ b/src/Tests/Grand.Web.Admin.Tests/MenuViewModelServiceTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.System.Admin; using Grand.Domain.Admin; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeProfile.cs index e218417f3..381e921cb 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Common; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeValueProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeValueProfile.cs index a73c7abc9..d2c4896e5 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeValueProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/AddressAttributeValueProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Common; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/AddressProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/AddressProfile.cs index eebd34e5d..1735b03c0 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/AddressProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/AddressProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Common; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/AddressSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/AddressSettingsProfile.cs index acdd0ca87..7ce3aaace 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/AddressSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/AddressSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/AdminSearchSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/AdminSearchSettingsProfile.cs index 57398b914..6efc3e86b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/AdminSearchSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/AdminSearchSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Admin; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/BlogCategoryProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/BlogCategoryProfile.cs index c70089311..ef6fe126b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/BlogCategoryProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/BlogCategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Blogs; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Blogs; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/BlogPostProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/BlogPostProfile.cs index 9cda0e7f8..dbb909669 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/BlogPostProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/BlogPostProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Blogs; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/BlogSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/BlogSettingsProfile.cs index 825a0ad26..2e801d937 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/BlogSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/BlogSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Blogs; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/BrandLayoutProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/BrandLayoutProfile.cs index 51c26ad35..2a93cad8c 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/BrandLayoutProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/BrandLayoutProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Layouts; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/BrandProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/BrandProfile.cs index 8b1645c05..fa54231dc 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/BrandProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/BrandProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CampaignProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CampaignProfile.cs index fcd0f4d53..83e15659a 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CampaignProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CampaignProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CaptchaSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CaptchaSettingsProfile.cs index 7e0f38870..0c8af6482 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CaptchaSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CaptchaSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CatalogSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CatalogSettingsProfile.cs index 6c3ba604e..772892864 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CatalogSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CatalogSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CategoryLayoutProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CategoryLayoutProfile.cs index 90db7a281..26ee90404 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CategoryLayoutProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CategoryLayoutProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Layouts; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CategoryProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CategoryProfile.cs index fbc08e403..e822d3dba 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CategoryProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CheckoutAttributeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CheckoutAttributeProfile.cs index fdcc68321..b8983688f 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CheckoutAttributeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CheckoutAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Orders; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CollectionLayoutProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CollectionLayoutProfile.cs index 586792f4d..854e05e4e 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CollectionLayoutProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CollectionLayoutProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Layouts; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CollectionProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CollectionProfile.cs index 045c3e0c7..12f8be825 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CollectionProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CollectionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CommonSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CommonSettingsProfile.cs index a7ff344bb..e079f423a 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CommonSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CommonSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ContactAttributeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ContactAttributeProfile.cs index 19c3706ab..1e3ba5769 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ContactAttributeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ContactAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ContactUsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ContactUsProfile.cs index 4e2b6f1d1..097509a51 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ContactUsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ContactUsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CountryProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CountryProfile.cs index c6b2f6dbe..9b311765b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CountryProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CountryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Directory; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Directory; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CourseLessonProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CourseLessonProfile.cs index 1469385da..f19ab117f 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CourseLessonProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CourseLessonProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Courses; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Courses; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CourseLevelProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CourseLevelProfile.cs index 0fe9021cf..f84680c7c 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CourseLevelProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CourseLevelProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Courses; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Courses; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CourseProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CourseProfile.cs index 0b1f63ff2..e2020679b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CourseProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CourseProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Courses; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Courses; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CourseSubjectProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CourseSubjectProfile.cs index 1ebec2ca8..85d98e36f 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CourseSubjectProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CourseSubjectProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Courses; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Courses; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CurrencyProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CurrencyProfile.cs index b13bd5d69..88ec83bf2 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CurrencyProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CurrencyProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Directory; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Directory; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeProfile.cs index b92aeeb4b..73c274f66 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Customers; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeValueProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeValueProfile.cs index 5a60600e1..6a83bbeae 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeValueProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CustomerAttributeValueProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Customers; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CustomerGroupProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CustomerGroupProfile.cs index 8fe3911bc..8ee19541a 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CustomerGroupProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CustomerGroupProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Customers; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CustomerSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CustomerSettingsProfile.cs index 38b442cef..620f7da0d 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CustomerSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CustomerSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/CustomerTagProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/CustomerTagProfile.cs index 71a7d552e..e7c9d577c 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/CustomerTagProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/CustomerTagProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Customers; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/DeliveryDateProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/DeliveryDateProfile.cs index 6a3c0336a..4a37aa439 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/DeliveryDateProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/DeliveryDateProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Shipping; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Shipping; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/DiscountProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/DiscountProfile.cs index 667b904c3..229730e9d 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/DiscountProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/DiscountProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Discounts; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Discounts; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/DocumentProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/DocumentProfile.cs index 087e3faf1..7e4728bb3 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/DocumentProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/DocumentProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Documents; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Documents; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/DocumentTypeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/DocumentTypeProfile.cs index 430a20eab..b644016e4 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/DocumentTypeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/DocumentTypeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Documents; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Documents; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/EmailAccountProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/EmailAccountProfile.cs index 45f8b51ee..f0e257525 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/EmailAccountProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/EmailAccountProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ExternalAuthenticationMethodProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ExternalAuthenticationMethodProfile.cs index e57592a8c..b30b6ded6 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ExternalAuthenticationMethodProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ExternalAuthenticationMethodProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.Authentication; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.ExternalAuthentication; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/GiftVoucherProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/GiftVoucherProfile.cs index 071d983d5..406c876b5 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/GiftVoucherProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/GiftVoucherProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Orders; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseCategoryProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseCategoryProfile.cs index b4f11d62b..005f24fd4 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseCategoryProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseCategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Knowledgebase; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Knowledgebase; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseSettingsProfile.cs index aa0915b58..4e1abe2ea 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/KnowledgebaseSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Knowledgebase; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/LanguageProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/LanguageProfile.cs index a3b64c08e..2fdfaf9bb 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/LanguageProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/LanguageProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Localization; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Localization; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/LoyaltyPointsSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/LoyaltyPointsSettingsProfile.cs index c67f8d3d5..c150dd680 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/LoyaltyPointsSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/LoyaltyPointsSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MeasureDimensionProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MeasureDimensionProfile.cs index d6ab12f3f..65a69c979 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MeasureDimensionProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MeasureDimensionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Directory; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Directory; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MeasureUnitProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MeasureUnitProfile.cs index aa2484b93..40e7bc5ee 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MeasureUnitProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MeasureUnitProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Directory; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Directory; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MeasureWeightProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MeasureWeightProfile.cs index 83625d1d0..e7a7fcc80 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MeasureWeightProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MeasureWeightProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Directory; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Directory; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MediaSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MediaSettingsProfile.cs index 7514f3a85..f5335f625 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MediaSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MediaSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Media; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MenuItemSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MenuItemSettingsProfile.cs index e7f5152d2..27521626f 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MenuItemSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MenuItemSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MenuProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MenuProfile.cs index 8f572433c..5d484c903 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MenuProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MenuProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Admin; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Menu; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnActionProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnActionProfile.cs index 96fa13afb..e03021761 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnActionProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnActionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnReasonProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnReasonProfile.cs index dd1bfe4a0..607a3cbf5 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnReasonProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MerchandiseReturnReasonProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/MessageTemplateProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/MessageTemplateProfile.cs index 2e4aa16ba..1f309da8b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/MessageTemplateProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/MessageTemplateProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/NewsItemProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/NewsItemProfile.cs index 29ec30a2f..268340239 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/NewsItemProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/NewsItemProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.News; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/NewsLetterSubscriptionProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/NewsLetterSubscriptionProfile.cs index e22d84a16..69c33411d 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/NewsLetterSubscriptionProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/NewsLetterSubscriptionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/NewsSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/NewsSettingsProfile.cs index 752a50e88..e9b0060f8 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/NewsSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/NewsSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.News; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/NewsletterCategoryProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/NewsletterCategoryProfile.cs index bfbfb5bf2..4d02ef4d3 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/NewsletterCategoryProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/NewsletterCategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/OrderSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/OrderSettingsProfile.cs index 1fb9bc5d7..ecfe54d30 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/OrderSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/OrderSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/OrderStatusProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/OrderStatusProfile.cs index 90a975d9e..f11f017b2 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/OrderStatusProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/OrderStatusProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Orders; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PageLayoutProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PageLayoutProfile.cs index adb92683a..1a5f6a128 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PageLayoutProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PageLayoutProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Pages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Layouts; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PageProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PageProfile.cs index 17b75dc40..92e810d93 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PageProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PageProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Pages; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PaymentMethodProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PaymentMethodProfile.cs index bc3bf03f6..ff7f9792b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PaymentMethodProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PaymentMethodProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.Checkout.Payments; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Payments; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PaymentSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PaymentSettingsProfile.cs index f05f6b894..84d8e4c31 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PaymentSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PaymentSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Payments; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Payments; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PdfSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PdfSettingsProfile.cs index d731cb9c1..8bf5ca104 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PdfSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PdfSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PermissionProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PermissionProfile.cs index de20d0df8..00e249ae5 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PermissionProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PermissionProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Permissions; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Permissions; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PickupPointProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PickupPointProfile.cs index 3eec8ccc2..2f3458642 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PickupPointProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PickupPointProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Shipping; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Shipping; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PluginDescriptorProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PluginDescriptorProfile.cs index 66811a366..e34bdff5a 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PluginDescriptorProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PluginDescriptorProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Infrastructure.Mapper; using Grand.Infrastructure.Plugins; using Grand.Web.AdminShared.Models.Plugins; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PredefinedProductAttributeValueProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PredefinedProductAttributeValueProfile.cs index 081f9a2e4..eb188adcd 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PredefinedProductAttributeValueProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PredefinedProductAttributeValueProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeCombinationProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeCombinationProfile.cs index 33e0bf9e3..25549b00f 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeCombinationProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeCombinationProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeMappingProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeMappingProfile.cs index d8f749e5d..cfe510ba1 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeMappingProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeMappingProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeProfile.cs index 89173d25d..f463cff82 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductProfile.cs index 1dc5dd187..144cec048 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductReviewProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductReviewProfile.cs index bf40836f5..0dcb2946f 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductReviewProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductReviewProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductSpecificationProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductSpecificationProfile.cs index 1e8481921..880402cd4 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductSpecificationProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductSpecificationProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ProductlayoutProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ProductlayoutProfile.cs index 18f5cb52b..d45848e6e 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ProductlayoutProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ProductlayoutProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Layouts; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/PushNotificationsSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/PushNotificationsSettingsProfile.cs index 8e40e3e4f..a38e82b44 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/PushNotificationsSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/PushNotificationsSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.PushNotifications; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/QueuedEmailProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/QueuedEmailProfile.cs index 314922d22..44afcc99a 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/QueuedEmailProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/QueuedEmailProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Messages; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Messages; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/SalesEmployeeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/SalesEmployeeProfile.cs index a1fb60bb5..e8816d1ed 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/SalesEmployeeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/SalesEmployeeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Customers; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ScheduleTaskProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ScheduleTaskProfile.cs index 7e993f22c..266498f59 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ScheduleTaskProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ScheduleTaskProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Tasks; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Tasks; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/SeoSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/SeoSettingsProfile.cs index 566d8959b..36caa9c34 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/SeoSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/SeoSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Seo; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ShippingMethodProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ShippingMethodProfile.cs index 68f29a7c3..53c02f22e 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ShippingMethodProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ShippingMethodProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Shipping; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Shipping; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ShippingRateComputationMethodProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ShippingRateComputationMethodProfile.cs index a42d39f71..f9d071500 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ShippingRateComputationMethodProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ShippingRateComputationMethodProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.Checkout.Shipping; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Shipping; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ShippingSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ShippingSettingsProfile.cs index 15720c7cd..7dbc787b3 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ShippingSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ShippingSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Shipping; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Shipping; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/ShoppingCartSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/ShoppingCartSettingsProfile.cs index 4ad05d7c5..9e93185e3 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/ShoppingCartSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/ShoppingCartSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Orders; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/SpecificationAttributeProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/SpecificationAttributeProfile.cs index 29935859b..773c14ca6 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/SpecificationAttributeProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/SpecificationAttributeProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/StateProvinceProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/StateProvinceProfile.cs index bccd3dbc3..f47539e7e 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/StateProvinceProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/StateProvinceProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Directory; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Directory; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/StoreInformationSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/StoreInformationSettingsProfile.cs index c56bf211e..684cf3d55 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/StoreInformationSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/StoreInformationSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Stores; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/StoreProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/StoreProfile.cs index bf91508d3..800bc75a2 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/StoreProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/StoreProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Stores; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Stores; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/TaxCategoryProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/TaxCategoryProfile.cs index 8c63c6aea..0eff01989 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/TaxCategoryProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/TaxCategoryProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Tax; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Tax; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/TaxProviderProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/TaxProviderProfile.cs index 237b505d2..df5f9fab1 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/TaxProviderProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/TaxProviderProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.Catalog.Tax; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Tax; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/TaxSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/TaxSettingsProfile.cs index 691199386..a77f313c0 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/TaxSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/TaxSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Tax; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Tax; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/TierPriceProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/TierPriceProfile.cs index 122c992e7..c9897a15b 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/TierPriceProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/TierPriceProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/UserApiProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/UserApiProfile.cs index 0c50983d1..f294f6107 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/UserApiProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/UserApiProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Customers; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Customers; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/VendorProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/VendorProfile.cs index e6cb42a9a..6af9f959e 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/VendorProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/VendorProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Vendors; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/VendorSettingsProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/VendorSettingsProfile.cs index 6771cc61a..e39b05e02 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/VendorSettingsProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/VendorSettingsProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Vendors; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Settings; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/WarehouseProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/WarehouseProfile.cs index 25480d39f..8289af28e 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/WarehouseProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/WarehouseProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Shipping; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Shipping; diff --git a/src/Web/Grand.Web.AdminShared/Mapper/WidgetPluginProfile.cs b/src/Web/Grand.Web.AdminShared/Mapper/WidgetPluginProfile.cs index 0e0483be0..ce3449cb5 100644 --- a/src/Web/Grand.Web.AdminShared/Mapper/WidgetPluginProfile.cs +++ b/src/Web/Grand.Web.AdminShared/Mapper/WidgetPluginProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.Cms; using Grand.Infrastructure.Mapper; using Grand.Web.AdminShared.Models.Cms; diff --git a/src/Web/Grand.Web.Vendor/Mapper/AddressProfile.cs b/src/Web/Grand.Web.Vendor/Mapper/AddressProfile.cs index 67bf47ae3..be8564ebb 100644 --- a/src/Web/Grand.Web.Vendor/Mapper/AddressProfile.cs +++ b/src/Web/Grand.Web.Vendor/Mapper/AddressProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Infrastructure.Mapper; using Grand.Web.Vendor.Models.Common; diff --git a/src/Web/Grand.Web.Vendor/Mapper/ProductProfile.cs b/src/Web/Grand.Web.Vendor/Mapper/ProductProfile.cs index d36c8e26c..f83ddd89a 100644 --- a/src/Web/Grand.Web.Vendor/Mapper/ProductProfile.cs +++ b/src/Web/Grand.Web.Vendor/Mapper/ProductProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Domain.Catalog; using Grand.Infrastructure.Mapper; diff --git a/src/Web/Grand.Web.Vendor/Mapper/VendorProfile.cs b/src/Web/Grand.Web.Vendor/Mapper/VendorProfile.cs index a744db012..57a96a54b 100644 --- a/src/Web/Grand.Web.Vendor/Mapper/VendorProfile.cs +++ b/src/Web/Grand.Web.Vendor/Mapper/VendorProfile.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Extensions; using Grand.Infrastructure.Mapper; using Grand.Web.Vendor.Models.Vendor; From 6a1525875d52ae96083bef0e450d28a94dff9c4c Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Thu, 26 Mar 2026 21:27:10 +0100 Subject: [PATCH 02/16] Refactor MappingCompiler: streamline property mapping logic and improve readability --- .../Grand.Mapping/Internal/MappingCompiler.cs | 320 ++++++++---------- 1 file changed, 133 insertions(+), 187 deletions(-) diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs index 2503c2b5e..8cdaf9bff 100644 --- a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -1,3 +1,4 @@ +using System.Collections; using System.Linq.Expressions; using System.Reflection; @@ -7,250 +8,195 @@ internal static class MappingCompiler { public static Action Compile(List configs) { - var srcParam = Expression.Parameter(typeof(TSource), "src"); - var dstParam = Expression.Parameter(typeof(TDest), "dst"); - var statements = new List(); + var steps = new List>(); - // Index non-path configs by member name (last ForMember wins for duplicates) var directConfigs = new Dictionary(StringComparer.Ordinal); foreach (var c in configs.Where(c => !c.IsPath)) directConfigs[c.MemberName] = c; - // Process all writable destination properties - var destProps = typeof(TDest) + var srcProps = typeof(TSource) .GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(p => p.CanWrite); + .Where(p => p.CanRead) + .ToDictionary(p => p.Name); - foreach (var destProp in destProps) + foreach (var destProp in typeof(TDest) + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.CanWrite)) { - if (directConfigs.TryGetValue(destProp.Name, out var config)) + if (directConfigs.TryGetValue(destProp.Name, out var mc)) { - if (config.IsIgnored) continue; + if (mc.IsIgnored) continue; - Expression? valueExpr; + var dp = destProp; + var cond = mc.ConditionExpression != null + ? (Func)mc.ConditionExpression.Compile() + : null; - if (config.MapFromExpression != null) + if (mc.MapFromExpression != null) { - // Custom mapping: inline the MapFrom lambda body with substituted src parameter - valueExpr = ParameterReplacer.Replace( - config.MapFromExpression.Body, - config.MapFromExpression.Parameters[0], - srcParam); + var getter = ToObjectGetter(mc.MapFromExpression); + steps.Add((src, dst) => + { + if (cond != null && !cond(src)) return; + dp.SetValue(dst, ConvertValue(getter(src), dp.PropertyType)); + }); } - else + else if (srcProps.TryGetValue(destProp.Name, out var sp)) { - // Condition-only: auto-map from same-named source property - var srcProp = typeof(TSource).GetProperty(destProp.Name, - BindingFlags.Public | BindingFlags.Instance); - if (srcProp == null || !srcProp.CanRead) continue; - valueExpr = Expression.Property(srcParam, srcProp); + var srcProp = sp; + steps.Add((src, dst) => + { + if (cond != null && !cond(src)) return; + dp.SetValue(dst, ConvertValue(srcProp.GetValue(src), dp.PropertyType)); + }); } - - valueExpr = TryConvert(valueExpr, destProp.PropertyType); - if (valueExpr == null) continue; - - var dstAccess = Expression.Property(dstParam, destProp); - var assignment = Expression.Assign(dstAccess, valueExpr); - statements.Add(WrapInCondition(assignment, config.ConditionExpression, srcParam)); } - else + else if (srcProps.TryGetValue(destProp.Name, out var srcProp)) { - // Auto-mapping: same-named property in source with compatible type - var srcProp = typeof(TSource).GetProperty(destProp.Name, - BindingFlags.Public | BindingFlags.Instance); - if (srcProp == null || !srcProp.CanRead) continue; - - var valueExpr = TryConvert( - Expression.Property(srcParam, srcProp), - destProp.PropertyType); - if (valueExpr == null) continue; - - var dstAccess = Expression.Property(dstParam, destProp); - statements.Add(Expression.Assign(dstAccess, valueExpr)); + var dp = destProp; + var sp = srcProp; + steps.Add((src, dst) => dp.SetValue(dst, ConvertValue(sp.GetValue(src), dp.PropertyType))); } } - // Process ForPath configs - foreach (var config in configs.Where(c => c.IsPath && !c.IsIgnored)) + // ForPath steps + foreach (var pc in configs.Where(c => c.IsPath && !c.IsIgnored + && c.MapFromExpression != null && c.DestinationPathExpression != null)) { - if (config.DestinationPathExpression == null || config.MapFromExpression == null) continue; - - var destAccess = ParameterReplacer.Replace( - config.DestinationPathExpression.Body, - config.DestinationPathExpression.Parameters[0], - dstParam); - - if (destAccess is not MemberExpression memberAccess) continue; + var setter = BuildPathSetter(pc.DestinationPathExpression!); + if (setter == null) continue; - var valueExpr = ParameterReplacer.Replace( - config.MapFromExpression.Body, - config.MapFromExpression.Parameters[0], - srcParam); + var getter = ToObjectGetter(pc.MapFromExpression!); + var cond = pc.ConditionExpression != null + ? (Func)pc.ConditionExpression.Compile() + : null; - valueExpr = TryConvert(valueExpr, memberAccess.Type) ?? valueExpr; - - var assignment = Expression.Assign(destAccess, valueExpr); - statements.Add(WrapInCondition(assignment, config.ConditionExpression, srcParam)); + steps.Add((src, dst) => + { + if (cond != null && !cond(src)) return; + setter(dst, getter(src)); + }); } - var body = statements.Count > 0 - ? (Expression)Expression.Block(statements) - : Expression.Empty(); - - return Expression.Lambda>(body, srcParam, dstParam).Compile(); + return (src, dst) => { foreach (var s in steps) s(src, dst); }; } - private static Expression? TryConvert(Expression expr, Type targetType) + // Compiles Expression> → Func to avoid DynamicInvoke. + private static Func ToObjectGetter(LambdaExpression expr) { - if (expr.Type == targetType) return expr; + var param = Expression.Parameter(typeof(TSource), "s"); + var body = Expression.Convert(Expression.Invoke(expr, param), typeof(object)); + return Expression.Lambda>(body, param).Compile(); + } - // T → Nullable: requires explicit Convert node for Expression.Assign. - if (targetType.IsGenericType - && targetType.GetGenericTypeDefinition() == typeof(Nullable<>) - && targetType.GetGenericArguments()[0] == expr.Type) + private static Action? BuildPathSetter(LambdaExpression pathExpr) + { + var members = new List(); + var node = pathExpr.Body; + while (node is MemberExpression me && me.Member is PropertyInfo pi) { - return Expression.Convert(expr, targetType); + members.Insert(0, pi); + node = me.Expression!; } + if (members.Count == 0) return null; + var finalProp = members[^1]; + if (!finalProp.CanWrite) return null; + var navigators = members.Take(members.Count - 1).ToArray(); - // Nullable → T: same reason. - var underlyingTarget = Nullable.GetUnderlyingType(targetType); - if (underlyingTarget != null && underlyingTarget == expr.Type) - return Expression.Convert(expr, targetType); + return (dst, val) => + { + object? target = dst; + foreach (var nav in navigators) + { + target = nav.GetValue(target); + if (target == null) return; + } + finalProp.SetValue(target, ConvertValue(val, finalProp.PropertyType)); + }; + } - // Collection coercions: IEnumerable/IList/etc. → T[] or List. - // AutoMapper handles these implicitly; we do the same via Enumerable.ToArray/ToList. - var collectionExpr = TryBuildCollectionConversion(expr, targetType); - if (collectionExpr != null) return collectionExpr; + // Runtime type coercion — mirrors AutoMapper defaults (AllowNullCollections=false). + internal static object? ConvertValue(object? val, Type targetType) + { + if (val == null) + return CreateEmptyCollection(targetType); // null → empty collection or null - // Direct upcast — no Convert node needed. - if (targetType.IsAssignableFrom(expr.Type)) return expr; + var srcType = val.GetType(); + if (targetType == srcType) return val; - // Numeric / enum value-type conversions. - if (IsNumericOrEnum(expr.Type) && IsNumericOrEnum(targetType)) + // T → Nullable: SetValue boxes it correctly, just return the value + var underlying = Nullable.GetUnderlyingType(targetType); + if (underlying != null) { - try { return Expression.Convert(expr, targetType); } - catch { return null; } + if (underlying == srcType || underlying.IsAssignableFrom(srcType)) return val; + try { return Convert.ChangeType(val, underlying); } catch { return null; } } - // User-defined conversion operator (Method != null). - // Do NOT fall back to a raw reference cast (Method == null) for incompatible - // reference types such as List → T[] — it compiles but throws InvalidCastException. - try + // Direct upcast / interface assignment + if (targetType.IsAssignableFrom(srcType)) { - var conv = Expression.Convert(expr, targetType); - return conv.Method != null ? conv : null; + // Concrete collection into an interface slot needs a List copy + // (e.g. string[] → IList must become List) + if (targetType.IsInterface && GetCollectionElementType(targetType) != null) + return CopyToList(val, GetCollectionElementType(targetType)!); + return val; } - catch - { - return null; - } - } - /// - /// Builds a null-safe collection conversion expression: - /// IEnumerable<T> → T[] via src == null ? null : Enumerable.ToArray(src) - /// IEnumerable<T> → List<T> via src == null ? null : Enumerable.ToList(src) - /// Returns null when the types are not a supported collection pair. - /// - private static Expression? TryBuildCollectionConversion(Expression expr, Type targetType) - { - // Determine source element type from IEnumerable - var srcElementType = GetEnumerableElementType(expr.Type); - if (srcElementType == null) return null; + // Cross-collection coercions: string[] ↔ List, IList → T[], etc. + var srcElem = GetCollectionElementType(srcType); + var dstElem = GetCollectionElementType(targetType); + if (srcElem != null && dstElem != null && srcElem == dstElem) + return CopyCollection(val, targetType, dstElem); - // Target: T[] - if (targetType.IsArray && targetType.GetArrayRank() == 1) - { - var destElementType = targetType.GetElementType()!; - if (srcElementType != destElementType) return null; + // Numeric / enum / primitive conversion + try { return Convert.ChangeType(val, targetType); } catch { return val; } + } - var toArray = typeof(Enumerable) - .GetMethod(nameof(Enumerable.ToArray))! - .MakeGenericMethod(destElementType); + private static object? CreateEmptyCollection(Type t) + { + if (t.IsArray && t.GetArrayRank() == 1) + return Array.CreateInstance(t.GetElementType()!, 0); + var elem = GetCollectionElementType(t); + if (elem != null) + return Activator.CreateInstance(typeof(List<>).MakeGenericType(elem)); + return null; + } - return BuildNullSafeCall(expr, targetType, toArray); - } + private static object CopyToList(object val, Type elemType) + { + var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elemType))!; + foreach (var item in (IEnumerable)val) list.Add(item); + return list; + } - // Target: List - if (targetType.IsGenericType - && targetType.GetGenericTypeDefinition() == typeof(List<>)) + private static object CopyCollection(object val, Type targetType, Type elemType) + { + var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elemType))!; + foreach (var item in (IEnumerable)val) list.Add(item); + if (targetType.IsArray) { - var destElementType = targetType.GetGenericArguments()[0]; - if (srcElementType != destElementType) return null; - - var toList = typeof(Enumerable) - .GetMethod(nameof(Enumerable.ToList))! - .MakeGenericMethod(destElementType); - - return BuildNullSafeCall(expr, targetType, toList); + var arr = Array.CreateInstance(elemType, list.Count); + list.CopyTo(arr, 0); + return arr; } - - return null; + return list; } - private static Type? GetEnumerableElementType(Type type) + private static Type? GetCollectionElementType(Type type) { - if (type == typeof(string)) return null; // string is IEnumerable, skip - if (type.IsArray) return type.GetElementType(); - - // Direct generic: IEnumerable, IList, ICollection, etc. + if (type == typeof(string)) return null; + if (type.IsArray && type.GetArrayRank() == 1) return type.GetElementType(); if (type.IsGenericType) { var def = type.GetGenericTypeDefinition(); - if (def == typeof(IEnumerable<>) || def == typeof(IList<>) - || def == typeof(ICollection<>) || def == typeof(IReadOnlyList<>) - || def == typeof(IReadOnlyCollection<>) || def == typeof(List<>)) - { + if (def == typeof(List<>) || def == typeof(IList<>) || def == typeof(ICollection<>) + || def == typeof(IEnumerable<>) || def == typeof(IReadOnlyList<>) + || def == typeof(IReadOnlyCollection<>)) return type.GetGenericArguments()[0]; - } } - - // Interface implemented: find IEnumerable foreach (var iface in type.GetInterfaces()) - { - if (iface.IsGenericType - && iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { + if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) return iface.GetGenericArguments()[0]; - } - } - return null; } - - // Generates: src == null ? (T[])null : Enumerable.ToArray(src) - private static Expression BuildNullSafeCall(Expression expr, Type targetType, MethodInfo method) - { - if (expr.Type.IsValueType) - return Expression.Call(method, expr); - - var iEnumType = method.GetParameters()[0].ParameterType; - var srcArg = expr.Type == iEnumType ? expr : Expression.Convert(expr, iEnumType); - - return Expression.Condition( - Expression.ReferenceEqual(expr, Expression.Constant(null, expr.Type)), - Expression.Constant(null, targetType), - Expression.Call(method, srcArg)); - } - - private static bool IsNumericOrEnum(Type t) - { - t = Nullable.GetUnderlyingType(t) ?? t; - return t.IsEnum || t == typeof(byte) || t == typeof(sbyte) - || t == typeof(short) || t == typeof(ushort) - || t == typeof(int) || t == typeof(uint) - || t == typeof(long) || t == typeof(ulong) - || t == typeof(float) || t == typeof(double) - || t == typeof(decimal); - } - - private static Expression WrapInCondition( - Expression body, - LambdaExpression? condition, - ParameterExpression srcParam) - { - if (condition == null) return body; - var condBody = ParameterReplacer.Replace(condition.Body, condition.Parameters[0], srcParam); - return Expression.IfThen(condBody, body); - } } From d32918e51eb3788156443f2fe4b674b719bdefe6 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Thu, 26 Mar 2026 21:48:42 +0100 Subject: [PATCH 03/16] Refactor MappingCompiler: optimize expression building and improve type coercion logic --- .../Grand.Mapping/Internal/MappingCompiler.cs | 253 ++++++++---------- 1 file changed, 118 insertions(+), 135 deletions(-) diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs index 8cdaf9bff..bb15ba8f1 100644 --- a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -1,4 +1,3 @@ -using System.Collections; using System.Linq.Expressions; using System.Reflection; @@ -8,195 +7,179 @@ internal static class MappingCompiler { public static Action Compile(List configs) { - var steps = new List>(); + var src = Expression.Parameter(typeof(TSource), "src"); + var dst = Expression.Parameter(typeof(TDest), "dst"); + var body = new List(); - var directConfigs = new Dictionary(StringComparer.Ordinal); + var direct = new Dictionary(StringComparer.Ordinal); foreach (var c in configs.Where(c => !c.IsPath)) - directConfigs[c.MemberName] = c; + direct[c.MemberName] = c; - var srcProps = typeof(TSource) - .GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(p => p.CanRead) - .ToDictionary(p => p.Name); - - foreach (var destProp in typeof(TDest) + foreach (var dp in typeof(TDest) .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanWrite)) { - if (directConfigs.TryGetValue(destProp.Name, out var mc)) + var destAccess = Expression.Property(dst, dp); + + if (direct.TryGetValue(dp.Name, out var mc)) { if (mc.IsIgnored) continue; - var dp = destProp; - var cond = mc.ConditionExpression != null - ? (Func)mc.ConditionExpression.Compile() - : null; - - if (mc.MapFromExpression != null) - { - var getter = ToObjectGetter(mc.MapFromExpression); - steps.Add((src, dst) => - { - if (cond != null && !cond(src)) return; - dp.SetValue(dst, ConvertValue(getter(src), dp.PropertyType)); - }); - } - else if (srcProps.TryGetValue(destProp.Name, out var sp)) - { - var srcProp = sp; - steps.Add((src, dst) => - { - if (cond != null && !cond(src)) return; - dp.SetValue(dst, ConvertValue(srcProp.GetValue(src), dp.PropertyType)); - }); - } + Expression? value = mc.MapFromExpression != null + ? Expression.Invoke(mc.MapFromExpression, src) + : SourceProp(src, dp.Name); + + if (value == null) continue; + value = Coerce(value, dp.PropertyType); + if (value == null) continue; + + var assign = Expression.Assign(destAccess, value); + body.Add(mc.ConditionExpression != null + ? Expression.IfThen(Expression.Invoke(mc.ConditionExpression, src), assign) + : (Expression)assign); } - else if (srcProps.TryGetValue(destProp.Name, out var srcProp)) + else { - var dp = destProp; - var sp = srcProp; - steps.Add((src, dst) => dp.SetValue(dst, ConvertValue(sp.GetValue(src), dp.PropertyType))); + var value = Coerce(SourceProp(src, dp.Name), dp.PropertyType); + if (value != null) + body.Add(Expression.Assign(destAccess, value)); } } - // ForPath steps + // ForPath configs foreach (var pc in configs.Where(c => c.IsPath && !c.IsIgnored && c.MapFromExpression != null && c.DestinationPathExpression != null)) { - var setter = BuildPathSetter(pc.DestinationPathExpression!); - if (setter == null) continue; + var destAccess = SubstitutePath(pc.DestinationPathExpression!, dst); + if (destAccess == null) continue; - var getter = ToObjectGetter(pc.MapFromExpression!); - var cond = pc.ConditionExpression != null - ? (Func)pc.ConditionExpression.Compile() - : null; + var value = Coerce(Expression.Invoke(pc.MapFromExpression!, src), destAccess.Type); + if (value == null) continue; - steps.Add((src, dst) => - { - if (cond != null && !cond(src)) return; - setter(dst, getter(src)); - }); + var assign = Expression.Assign(destAccess, value); + body.Add(pc.ConditionExpression != null + ? Expression.IfThen(Expression.Invoke(pc.ConditionExpression, src), assign) + : (Expression)assign); } - return (src, dst) => { foreach (var s in steps) s(src, dst); }; + return Expression.Lambda>( + body.Count > 0 ? (Expression)Expression.Block(body) : Expression.Empty(), + src, dst).Compile(); } - // Compiles Expression> → Func to avoid DynamicInvoke. - private static Func ToObjectGetter(LambdaExpression expr) + // Returns Expression for same-named readable property on src, or null. + private static Expression? SourceProp(ParameterExpression src, string name) { - var param = Expression.Parameter(typeof(TSource), "s"); - var body = Expression.Convert(Expression.Invoke(expr, param), typeof(object)); - return Expression.Lambda>(body, param).Compile(); + var sp = src.Type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); + return sp?.CanRead == true ? Expression.Property(src, sp) : null; } - private static Action? BuildPathSetter(LambdaExpression pathExpr) + // Rebuilds (TDest d) => d.Outer.Inner with our dst parameter — no ExpressionVisitor needed. + private static MemberExpression? SubstitutePath(LambdaExpression pathExpr, ParameterExpression dst) { - var members = new List(); + var chain = new List(); var node = pathExpr.Body; while (node is MemberExpression me && me.Member is PropertyInfo pi) { - members.Insert(0, pi); + chain.Insert(0, pi); node = me.Expression!; } - if (members.Count == 0) return null; - var finalProp = members[^1]; - if (!finalProp.CanWrite) return null; - var navigators = members.Take(members.Count - 1).ToArray(); + if (chain.Count == 0 || node is not ParameterExpression) return null; - return (dst, val) => - { - object? target = dst; - foreach (var nav in navigators) - { - target = nav.GetValue(target); - if (target == null) return; - } - finalProp.SetValue(target, ConvertValue(val, finalProp.PropertyType)); - }; + Expression result = dst; + foreach (var pi in chain) + result = Expression.Property(result, pi); + return result as MemberExpression; } - // Runtime type coercion — mirrors AutoMapper defaults (AllowNullCollections=false). - internal static object? ConvertValue(object? val, Type targetType) + // Build-time type coercion inside Expression Tree. Returns null → skip property. + private static Expression? Coerce(Expression? expr, Type target) { - if (val == null) - return CreateEmptyCollection(targetType); // null → empty collection or null - - var srcType = val.GetType(); - if (targetType == srcType) return val; - - // T → Nullable: SetValue boxes it correctly, just return the value - var underlying = Nullable.GetUnderlyingType(targetType); - if (underlying != null) - { - if (underlying == srcType || underlying.IsAssignableFrom(srcType)) return val; - try { return Convert.ChangeType(val, underlying); } catch { return null; } - } - - // Direct upcast / interface assignment - if (targetType.IsAssignableFrom(srcType)) - { - // Concrete collection into an interface slot needs a List copy - // (e.g. string[] → IList must become List) - if (targetType.IsInterface && GetCollectionElementType(targetType) != null) - return CopyToList(val, GetCollectionElementType(targetType)!); - return val; - } - - // Cross-collection coercions: string[] ↔ List, IList → T[], etc. - var srcElem = GetCollectionElementType(srcType); - var dstElem = GetCollectionElementType(targetType); + if (expr == null) return null; + if (expr.Type == target) return expr; + + // T → Nullable + var underlyingTarget = Nullable.GetUnderlyingType(target); + if (underlyingTarget == expr.Type) + return Expression.Convert(expr, target); + + // Nullable → T + var underlyingSource = Nullable.GetUnderlyingType(expr.Type); + if (underlyingSource == target) + return Expression.Convert(expr, target); + + // Collection coercions with null guard (AutoMapper AllowNullCollections=false behaviour). + // Runs before IsAssignableFrom to avoid IList/T[] cross-type issues. + var srcElem = CollectionElementType(expr.Type); + var dstElem = CollectionElementType(target); if (srcElem != null && dstElem != null && srcElem == dstElem) - return CopyCollection(val, targetType, dstElem); + return BuildCollectionCoerce(expr, target, dstElem); - // Numeric / enum / primitive conversion - try { return Convert.ChangeType(val, targetType); } catch { return val; } - } + // Direct upcast (no Convert node needed for reference types / value subtypes) + if (target.IsAssignableFrom(expr.Type)) return expr; - private static object? CreateEmptyCollection(Type t) - { - if (t.IsArray && t.GetArrayRank() == 1) - return Array.CreateInstance(t.GetElementType()!, 0); - var elem = GetCollectionElementType(t); - if (elem != null) - return Activator.CreateInstance(typeof(List<>).MakeGenericType(elem)); - return null; - } + // Numeric / enum value-type conversion + if (IsNumericOrEnum(expr.Type) && IsNumericOrEnum(target)) + try { return Expression.Convert(expr, target); } catch { return null; } - private static object CopyToList(object val, Type elemType) - { - var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elemType))!; - foreach (var item in (IEnumerable)val) list.Add(item); - return list; + // User-defined conversion operator only (Method != null prevents reference downcast) + try { var c = Expression.Convert(expr, target); return c.Method != null ? c : null; } + catch { return null; } } - private static object CopyCollection(object val, Type targetType, Type elemType) + // null source → empty collection; non-null source → ToArray/ToList copy. + private static Expression BuildCollectionCoerce(Expression src, Type target, Type elem) { - var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elemType))!; - foreach (var item in (IEnumerable)val) list.Add(item); - if (targetType.IsArray) + var iEnum = typeof(IEnumerable<>).MakeGenericType(elem); + var srcCast = src.Type == iEnum ? src : Expression.Convert(src, iEnum); + + Expression filled, empty; + if (target.IsArray) + { + var toArray = typeof(Enumerable).GetMethod(nameof(Enumerable.ToArray))!.MakeGenericMethod(elem); + filled = Expression.Call(toArray, srcCast); + empty = Expression.NewArrayBounds(elem, Expression.Constant(0)); + } + else { - var arr = Array.CreateInstance(elemType, list.Count); - list.CopyTo(arr, 0); - return arr; + var listType = typeof(List<>).MakeGenericType(elem); + var toList = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList))!.MakeGenericMethod(elem); + filled = Expression.Call(toList, srcCast); + empty = Expression.New(listType); } - return list; + + if (src.Type.IsValueType) return filled; + return Expression.Condition( + Expression.ReferenceEqual(src, Expression.Constant(null, src.Type)), + empty, filled); } - private static Type? GetCollectionElementType(Type type) + private static Type? CollectionElementType(Type t) { - if (type == typeof(string)) return null; - if (type.IsArray && type.GetArrayRank() == 1) return type.GetElementType(); - if (type.IsGenericType) + if (t == typeof(string)) return null; + if (t.IsArray && t.GetArrayRank() == 1) return t.GetElementType(); + if (t.IsGenericType) { - var def = type.GetGenericTypeDefinition(); + var def = t.GetGenericTypeDefinition(); if (def == typeof(List<>) || def == typeof(IList<>) || def == typeof(ICollection<>) || def == typeof(IEnumerable<>) || def == typeof(IReadOnlyList<>) || def == typeof(IReadOnlyCollection<>)) - return type.GetGenericArguments()[0]; + return t.GetGenericArguments()[0]; } - foreach (var iface in type.GetInterfaces()) + foreach (var iface in t.GetInterfaces()) if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) return iface.GetGenericArguments()[0]; return null; } + + private static bool IsNumericOrEnum(Type t) + { + t = Nullable.GetUnderlyingType(t) ?? t; + return t.IsEnum || t == typeof(byte) || t == typeof(sbyte) + || t == typeof(short) || t == typeof(ushort) + || t == typeof(int) || t == typeof(uint) + || t == typeof(long) || t == typeof(ulong) + || t == typeof(float) || t == typeof(double) + || t == typeof(decimal); + } } From 5a1e007b3b865c6821d011ddf106bc674d364f7f Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Fri, 27 Mar 2026 19:08:28 +0100 Subject: [PATCH 04/16] Add Grand.Mapping.Tests --- Directory.Packages.props | 5 +- GrandNode.sln | 29 + ...sts.BrandLayoutModel_ToDomain.verified.txt | 6 + ...pingTests.BrandLayout_ToModel.verified.txt | 6 + ....CategoryLayoutModel_ToDomain.verified.txt | 6 + ...gTests.CategoryLayout_ToModel.verified.txt | 6 + ...ollectionLayoutModel_ToDomain.verified.txt | 6 + ...ests.CollectionLayout_ToModel.verified.txt | 6 + ...ests.PageLayoutModel_ToDomain.verified.txt | 6 + ...ppingTests.PageLayout_ToModel.verified.txt | 6 + ...s.ProductLayoutModel_ToDomain.verified.txt | 6 + ...ngTests.ProductLayout_ToModel.verified.txt | 6 + .../AdminShared/CatalogLayoutMappingTests.cs | 108 ++++ ...pingTests.BrandModel_ToDomain.verified.txt | 30 + ...logMappingTests.Brand_ToModel.verified.txt | 22 + ...gTests.CategoryModel_ToDomain.verified.txt | 36 ++ ...MappingTests.Category_ToModel.verified.txt | 28 + ...ests.CollectionModel_ToDomain.verified.txt | 18 + ...ppingTests.Collection_ToModel.verified.txt | 22 + ...ts.DeliveryDateModel_ToDomain.verified.txt | 6 + ...ingTests.DeliveryDate_ToModel.verified.txt | 6 + ...gTests.DiscountModel_ToDomain.verified.txt | 19 + ...MappingTests.Discount_ToModel.verified.txt | 19 + ...pingTests.GiftVoucher_ToModel.verified.txt | 13 + .../AdminShared/CatalogMappingTests.cs | 310 +++++++++ ...dAttributeValueModel_ToDomain.verified.txt | 9 + ...definedAttributeValue_ToModel.verified.txt | 11 + ...Value_ToProductAttributeValue.verified.txt | 8 + ...buteCombinationModel_ToDomain.verified.txt | 9 + ...tAttributeCombination_ToModel.verified.txt | 11 + ...ttributeMappingModel_ToDomain.verified.txt | 10 + ...oductAttributeMapping_ToModel.verified.txt | 13 + ...roductAttributeModel_ToDomain.verified.txt | 9 + ...ests.ProductAttribute_ToModel.verified.txt | 5 + ...ngTests.ProductModel_ToDomain.verified.txt | 51 ++ ...s.ProductReviewModel_ToDomain.verified.txt | 11 + ...ctSpecificationModel_ToDomain.verified.txt | 10 + ....ProductSpecification_ToModel.verified.txt | 10 + ...tMappingTests.Product_ToModel.verified.txt | 76 +++ ...cationAttributeModel_ToDomain.verified.txt | 9 + ...AttributeOptionModel_ToDomain.verified.txt | 6 + ...cationAttributeOption_ToModel.verified.txt | 8 + ...pecificationAttribute_ToModel.verified.txt | 6 + ...Tests.TierPriceModel_ToDomain.verified.txt | 8 + ...appingTests.TierPrice_ToModel.verified.txt | 8 + .../AdminShared/CatalogProductMappingTests.cs | 357 +++++++++++ ...ddressAttributeModel_ToDomain.verified.txt | 8 + ...sAttributeValueModel_ToDomain.verified.txt | 6 + ...AddressAttributeValue_ToModel.verified.txt | 6 + ...ests.AddressAttribute_ToModel.verified.txt | 7 + ...ngTests.AddressModel_ToDomain.verified.txt | 14 + ...nMappingTests.Address_ToModel.verified.txt | 42 ++ ...pingTests.BankAccount_ToModel.verified.txt | 7 + ...ngTests.CountryModel_ToDomain.verified.txt | 16 + ...nMappingTests.Country_ToModel.verified.txt | 13 + ...gTests.CurrencyModel_ToDomain.verified.txt | 13 + ...MappingTests.Currency_ToModel.verified.txt | 13 + ...gTests.DocumentModel_ToDomain.verified.txt | 15 + ...ts.DocumentTypeModel_ToDomain.verified.txt | 6 + ...ingTests.DocumentType_ToModel.verified.txt | 6 + ...MappingTests.Document_ToModel.verified.txt | 12 + ...ests.DomainHostModel_ToDomain.verified.txt | 5 + ...ppingTests.DomainHost_ToModel.verified.txt | 5 + ...gTests.LanguageModel_ToDomain.verified.txt | 14 + ...MappingTests.Language_ToModel.verified.txt | 12 + ...easureDimensionModel_ToDomain.verified.txt | 7 + ...ests.MeasureDimension_ToModel.verified.txt | 8 + ...sts.MeasureUnitModel_ToDomain.verified.txt | 5 + ...pingTests.MeasureUnit_ToModel.verified.txt | 5 + ...s.MeasureWeightModel_ToDomain.verified.txt | 7 + ...ngTests.MeasureWeight_ToModel.verified.txt | 8 + ...iseReturnActionModel_ToDomain.verified.txt | 5 + ...rchandiseReturnAction_ToModel.verified.txt | 5 + ...iseReturnReasonModel_ToDomain.verified.txt | 5 + ...rchandiseReturnReason_ToModel.verified.txt | 5 + ...sts.OrderStatusModel_ToDomain.verified.txt | 6 + ...pingTests.OrderStatus_ToModel.verified.txt | 5 + ...ermissionCreateModel_ToDomain.verified.txt | 7 + ...ests.Permission_ToUpdateModel.verified.txt | 7 + ...sts.PickupPointModel_ToDomain.verified.txt | 10 + ...pingTests.PickupPoint_ToModel.verified.txt | 11 + ...s.StateProvinceModel_ToDomain.verified.txt | 7 + ...ngTests.StateProvince_ToModel.verified.txt | 7 + ...pingTests.StoreModel_ToDomain.verified.txt | 9 + ...monMappingTests.Store_ToModel.verified.txt | 26 + ...sts.TaxCategoryModel_ToDomain.verified.txt | 5 + ...pingTests.TaxCategory_ToModel.verified.txt | 5 + ...Tests.WarehouseModel_ToDomain.verified.txt | 10 + ...appingTests.Warehouse_ToModel.verified.txt | 37 ++ .../AdminShared/CommonMappingTests.cs | 594 ++++++++++++++++++ ...ts.BlogCategoryModel_ToDomain.verified.txt | 10 + ...ingTests.BlogCategory_ToModel.verified.txt | 6 + ...gTests.BlogPostModel_ToDomain.verified.txt | 12 + ...MappingTests.BlogPost_ToModel.verified.txt | 12 + ...ts.BlogSettingsModel_ToDomain.verified.txt | 8 + ...ingTests.BlogSettings_ToModel.verified.txt | 8 + ...ts.CourseLessonModel_ToDomain.verified.txt | 9 + ...ingTests.CourseLesson_ToModel.verified.txt | 11 + ...sts.CourseLevelModel_ToDomain.verified.txt | 5 + ...pingTests.CourseLevel_ToModel.verified.txt | 5 + ...ingTests.CourseModel_ToDomain.verified.txt | 17 + ...s.CourseSubjectModel_ToDomain.verified.txt | 6 + ...ngTests.CourseSubject_ToModel.verified.txt | 6 + ...ntMappingTests.Course_ToModel.verified.txt | 11 + ...edgebaseArticleModel_ToDomain.verified.txt | 18 + ....KnowledgebaseArticle_ToModel.verified.txt | 11 + ...dgebaseCategoryModel_ToDomain.verified.txt | 15 + ...KnowledgebaseCategory_ToModel.verified.txt | 10 + ...dgebaseSettingsModel_ToDomain.verified.txt | 5 + ...KnowledgebaseSettings_ToModel.verified.txt | 5 + ...gTests.NewsItemModel_ToDomain.verified.txt | 16 + ...MappingTests.NewsItem_ToModel.verified.txt | 12 + ...ts.NewsSettingsModel_ToDomain.verified.txt | 7 + ...ingTests.NewsSettings_ToModel.verified.txt | 7 + ...ppingTests.PageModel_ToDomain.verified.txt | 23 + ...tentMappingTests.Page_ToModel.verified.txt | 20 + .../AdminShared/ContentMappingTests.cs | 390 ++++++++++++ ...ontactAttributeModel_ToDomain.verified.txt | 16 + ...tAttributeValueModel_ToDomain.verified.txt | 6 + ...ContactAttributeValue_ToModel.verified.txt | 7 + ...ests.ContactAttribute_ToModel.verified.txt | 9 + ...appingTests.ContactUs_ToModel.verified.txt | 8 + ...stomerAttributeModel_ToDomain.verified.txt | 8 + ...rAttributeValueModel_ToDomain.verified.txt | 6 + ...ustomerAttributeValue_ToModel.verified.txt | 6 + ...sts.CustomerAttribute_ToModel.verified.txt | 8 + ...s.CustomerGroupModel_ToDomain.verified.txt | 10 + ...ngTests.CustomerGroup_ToModel.verified.txt | 10 + ...sts.CustomerTagModel_ToDomain.verified.txt | 4 + ...pingTests.CustomerTag_ToModel.verified.txt | 4 + ...terSubscriptionModel_ToDomain.verified.txt | 5 + ...ewsLetterSubscription_ToModel.verified.txt | 6 + ...sletterCategoryModel_ToDomain.verified.txt | 11 + ...ts.NewsletterCategory_ToModel.verified.txt | 7 + ...s.SalesEmployeeModel_ToDomain.verified.txt | 7 + ...ngTests.SalesEmployee_ToModel.verified.txt | 7 + ...s.UserApiCreateModel_ToDomain.verified.txt | 6 + ...ngTests.UserApiModel_ToDomain.verified.txt | 5 + ...rMappingTests.UserApi_ToModel.verified.txt | 5 + .../AdminShared/CustomerMappingTests.cs | 319 ++++++++++ ...uthenticationProvider_ToModel.verified.txt | 5 + ...Tests.PaymentProvider_ToModel.verified.txt | 10 + ...ppingTests.PluginInfo_ToModel.verified.txt | 8 + ...teCalculationProvider_ToModel.verified.txt | 7 + ...pingTests.TaxProvider_ToModel.verified.txt | 5 + ...gTests.WidgetProvider_ToModel.verified.txt | 5 + .../AdminShared/ProviderMappingTests.cs | 122 ++++ ...AddressSettingsModel_ToDomain.verified.txt | 24 + ...Tests.AddressSettings_ToModel.verified.txt | 24 + ...nSearchSettingsModel_ToDomain.verified.txt | 13 + ...s.AdminSearchSettings_ToModel.verified.txt | 13 + ...Tests.CaptchaSettings_ToModel.verified.txt | 19 + ...CatalogSettingsModel_ToDomain.verified.txt | 57 ++ ...Tests.CatalogSettings_ToModel.verified.txt | 74 +++ ....CommonSettingsModel_ToDomain.verified.txt | 13 + ...gTests.CommonSettings_ToModel.verified.txt | 12 + ...ustomerSettingsModel_ToDomain.verified.txt | 50 ++ ...ests.CustomerSettings_ToModel.verified.txt | 48 ++ ...yPointsSettingsModel_ToDomain.verified.txt | 8 + ...LoyaltyPointsSettings_ToModel.verified.txt | 10 + ...s.MediaSettingsModel_ToDomain.verified.txt | 6 + ...ngTests.MediaSettings_ToModel.verified.txt | 18 + ...s.OrderSettingsModel_ToDomain.verified.txt | 23 + ...ngTests.OrderSettings_ToModel.verified.txt | 24 + ...cationsSettingsModel_ToDomain.verified.txt | 4 + ...NotificationsSettings_ToModel.verified.txt | 11 + ...ecuritySettingsModel_ToDomain.verified.txt | 18 + ...sts.SeoSettingsModel_ToDomain.verified.txt | 12 + ...pingTests.SeoSettings_ToModel.verified.txt | 14 + ...ingCartSettingsModel_ToDomain.verified.txt | 21 + ....ShoppingCartSettings_ToModel.verified.txt | 23 + ...sts.TaxSettingsModel_ToDomain.verified.txt | 21 + ...pingTests.TaxSettings_ToModel.verified.txt | 48 ++ .../AdminShared/SettingsMappingTests.cs | 452 +++++++++++++ ...eckoutAttributeModel_ToDomain.verified.txt | 18 + ...tAttributeValueModel_ToDomain.verified.txt | 7 + ...heckoutAttributeValue_ToModel.verified.txt | 8 + ...sts.CheckoutAttribute_ToModel.verified.txt | 12 + ...PaymentSettingsModel_ToDomain.verified.txt | 7 + ...Tests.PaymentSettings_ToModel.verified.txt | 7 + ....ShippingMethodModel_ToDomain.verified.txt | 6 + ...gTests.ShippingMethod_ToModel.verified.txt | 6 + ...hippingSettingsModel_ToDomain.verified.txt | 11 + ...ests.ShippingSettings_ToModel.verified.txt | 11 + .../AdminShared/ShippingMappingTests.cs | 170 +++++ ...ests.AdminSiteMap_ToMenuModel.verified.txt | 12 + ...gTests.CampaignModel_ToDomain.verified.txt | 7 + ...MappingTests.Campaign_ToModel.verified.txt | 7 + ...ts.EmailAccountModel_ToDomain.verified.txt | 9 + ...ingTests.EmailAccount_ToModel.verified.txt | 10 + ...ests.MenuModel_ToAdminSiteMap.verified.txt | 11 + ...MessageTemplateModel_ToDomain.verified.txt | 12 + ...Tests.MessageTemplate_ToModel.verified.txt | 15 + ...sts.QueuedEmailModel_ToDomain.verified.txt | 7 + ...pingTests.QueuedEmail_ToModel.verified.txt | 13 + ...ts.ScheduleTaskModel_ToDomain.verified.txt | 6 + ...ingTests.ScheduleTask_ToModel.verified.txt | 8 + ...ingTests.VendorModel_ToDomain.verified.txt | 14 + ....VendorSettingsModel_ToDomain.verified.txt | 57 ++ ...gTests.VendorSettings_ToModel.verified.txt | 35 ++ ...emMappingTests.Vendor_ToModel.verified.txt | 45 ++ .../AdminShared/SystemMappingTests.cs | 287 +++++++++ ...pingTests.AddressDto_ToEntity.verified.txt | 11 + ...ApiMappingTests.Address_ToDto.verified.txt | 11 + ...appingTests.BrandDto_ToEntity.verified.txt | 17 + .../ApiMappingTests.Brand_ToDto.verified.txt | 14 + ...ingTests.CategoryDto_ToEntity.verified.txt | 18 + ...piMappingTests.Category_ToDto.verified.txt | 15 + ...gTests.CollectionDto_ToEntity.verified.txt | 16 + ...MappingTests.Collection_ToDto.verified.txt | 13 + ...ingTests.CustomerDto_ToEntity.verified.txt | 12 + ...sts.CustomerGroupDto_ToEntity.verified.txt | 10 + ...pingTests.CustomerGroup_ToDto.verified.txt | 10 + ...ustomer_ToDto_EmptyUserFields.verified.txt | 10 + ...pingTests.PictureDto_ToEntity.verified.txt | 8 + ...ApiMappingTests.Picture_ToDto.verified.txt | 8 + ...edProductAttributeValue_ToDto.verified.txt | 6 + ....ProductAttributeDto_ToEntity.verified.txt | 6 + ...tAttributeMappingDto_ToEntity.verified.txt | 9 + ...ProductAttributeMapping_ToDto.verified.txt | 7 + ...gTests.ProductAttribute_ToDto.verified.txt | 5 + ...pingTests.ProductDto_ToEntity.verified.txt | 44 ++ ....ProductTierPriceDto_ToEntity.verified.txt | 7 + ...ApiMappingTests.Product_ToDto.verified.txt | 41 ++ ...ficationAttributeDto_ToEntity.verified.txt | 6 + ...ficationAttributeOption_ToDto.verified.txt | 5 + ....SpecificationAttribute_ToDto.verified.txt | 5 + ...iMappingTests.TierPrice_ToDto.verified.txt | 7 + .../Api/ApiMappingTests.cs | 438 +++++++++++++ ...ity_NullConditions_NotApplied.verified.txt | 12 + ...o_ToEntity_WithNullableValues.verified.txt | 19 + ...ity_NullConditions_NotApplied.verified.txt | 15 + ...o_ToEntity_WithNullableValues.verified.txt | 23 + ...ity_NullConditions_NotApplied.verified.txt | 13 + ...o_ToEntity_WithNullableValues.verified.txt | 20 + ...tDto_ToEntity_BasicProperties.verified.txt | 45 ++ ..._ExistingIdPreservedWhenEmpty.verified.txt | 40 ++ .../Catalog/ExportImportMappingTests.cs | 177 ++++++ .../Grand.Mapping.Tests.csproj | 27 + ....Address_ToVendorAddressModel.verified.txt | 38 ++ ...buteCombination_ToVendorModel.verified.txt | 9 + ...ttributeMapping_ToVendorModel.verified.txt | 12 + ....Product_ToVendorProductModel.verified.txt | 57 ++ ...ingTests.VendorModel_ToVendor.verified.txt | 17 + ....VendorProductModel_ToProduct.verified.txt | 44 ++ ...ingTests.Vendor_ToVendorModel.verified.txt | 43 ++ .../Vendor/VendorMappingTests.cs | 160 +++++ src/Tests/Grand.Mapping.Tests/VerifyConfig.cs | 33 + 248 files changed, 7148 insertions(+), 2 deletions(-) create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayoutModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayout_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayoutModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayout_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayoutModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayout_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayoutModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayout_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayoutModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayout_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.BrandModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Brand_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CategoryModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Category_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CollectionModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Collection_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDateModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDate_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DiscountModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.GiftVoucher_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValueModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToProductAttributeValue.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombinationModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMappingModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMapping_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttribute_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductReviewModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecificationModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecification_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOptionModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOption_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttribute_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPriceModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPrice_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValueModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValue_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttribute_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Address_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.BankAccount_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CountryModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Country_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CurrencyModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Currency_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentTypeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentType_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Document_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHostModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHost_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.LanguageModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Language_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimensionModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimension_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnitModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnit_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeightModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeight_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnActionModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnAction_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReasonModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReason_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatusModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatus_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PermissionCreateModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Permission_ToUpdateModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPointModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvinceModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvince_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategoryModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategory_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.WarehouseModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Warehouse_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategoryModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategory_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPostModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPost_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLessonModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLesson_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevelModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevel_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubjectModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubject_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Course_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticleModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticle_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategoryModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategory_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItemModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItem_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.PageModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Page_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValueModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValue_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttribute_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactUs_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValueModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValue_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttribute_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroupModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroup_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTagModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTag_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscriptionModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategoryModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategory_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployeeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployee_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiCreateModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApi_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ExternalAuthenticationProvider_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PaymentProvider_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PluginInfo_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ShippingRateCalculationProvider_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.TaxProvider_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.WidgetProvider_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CaptchaSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SecuritySettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValueModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValue_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttribute_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethodModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethod_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.AdminSiteMap_ToMenuModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.CampaignModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Campaign_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccountModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccount_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MenuModel_ToAdminSiteMap.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplateModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplate_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmailModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmail_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTaskModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTask_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Vendor_ToModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.AddressDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Address_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.BrandDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Brand_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CategoryDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Category_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CollectionDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Collection_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroupDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroup_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_EmptyUserFields.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PictureDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Picture_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PredefinedProductAttributeValue_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMappingDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMapping_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttribute_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductTierPriceDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Product_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeDto_ToEntity.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeOption_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttribute_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.TierPrice_ToDto.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_NullConditions_NotApplied.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_WithNullableValues.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_NullConditions_NotApplied.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_WithNullableValues.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_NullConditions_NotApplied.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_WithNullableValues.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_BasicProperties.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_ExistingIdPreservedWhenEmpty.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/Grand.Mapping.Tests.csproj create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Address_ToVendorAddressModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeMapping_ToVendorModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Product_ToVendorProductModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorModel_ToVendor.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorProductModel_ToProduct.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Vendor_ToVendorModel.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs create mode 100644 src/Tests/Grand.Mapping.Tests/VerifyConfig.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 5f787c5f7..f5a100be7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -59,10 +59,11 @@ - - + + + diff --git a/GrandNode.sln b/GrandNode.sln index c6bd60b9f..2fe76ecdd 100644 --- a/GrandNode.sln +++ b/GrandNode.sln @@ -145,6 +145,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.ServiceDefaults", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grand.Web.AdminShared", "src\Web\Grand.Web.AdminShared\Grand.Web.AdminShared.csproj", "{12C4A556-E62E-4EC0-BCD9-E9D4E1F3D430}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CEA09484-30F6-4D44-02F6-822E06DBC57C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grand.Mapping.Tests", "src\Tests\Grand.Mapping.Tests\Grand.Mapping.Tests.csproj", "{396E6929-5365-4116-8AA8-DF5327247798}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{03997797-E7F5-0643-168D-B8EA7178C2FE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{EC447DCF-ABFA-6E24-52A5-D7FD48A5C558}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Business", "Business", "{CC1F4FA2-92F2-BA4B-815B-7EC2CBBCCF50}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -863,6 +875,18 @@ Global {12C4A556-E62E-4EC0-BCD9-E9D4E1F3D430}.Release|x64.Build.0 = Release|Any CPU {12C4A556-E62E-4EC0-BCD9-E9D4E1F3D430}.Release|x86.ActiveCfg = Release|Any CPU {12C4A556-E62E-4EC0-BCD9-E9D4E1F3D430}.Release|x86.Build.0 = Release|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Debug|Any CPU.Build.0 = Debug|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Debug|x64.ActiveCfg = Debug|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Debug|x64.Build.0 = Debug|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Debug|x86.ActiveCfg = Debug|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Debug|x86.Build.0 = Debug|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Release|Any CPU.ActiveCfg = Release|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Release|Any CPU.Build.0 = Release|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Release|x64.ActiveCfg = Release|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Release|x64.Build.0 = Release|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Release|x86.ActiveCfg = Release|Any CPU + {396E6929-5365-4116-8AA8-DF5327247798}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -926,6 +950,11 @@ Global {E0B26803-010B-4198-8016-002242076B61} = {583FFBBD-421C-4FB7-BBDA-F9CAF35A354A} {136F1E35-8B20-465C-8D42-30A5A0D0DA1F} = {583FFBBD-421C-4FB7-BBDA-F9CAF35A354A} {12C4A556-E62E-4EC0-BCD9-E9D4E1F3D430} = {BF4543A8-0731-4FDD-BB6B-0ADB9561F981} + {CEA09484-30F6-4D44-02F6-822E06DBC57C} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {396E6929-5365-4116-8AA8-DF5327247798} = {CEA09484-30F6-4D44-02F6-822E06DBC57C} + {03997797-E7F5-0643-168D-B8EA7178C2FE} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {CC1F4FA2-92F2-BA4B-815B-7EC2CBBCCF50} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {88B478F4-FD3B-4C24-9E84-4FAAF0254397} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayoutModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayoutModel_ToDomain.verified.txt new file mode 100644 index 000000000..fdfdf853c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayoutModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: BrandDefault, + ViewPath: BrandTemplate.Simple, + DisplayOrder: 2, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayout_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayout_ToModel.verified.txt new file mode 100644 index 000000000..bd0615d43 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.BrandLayout_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: BrandDefault, + ViewPath: BrandTemplate.Simple, + DisplayOrder: 2, + Id: bl-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayoutModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayoutModel_ToDomain.verified.txt new file mode 100644 index 000000000..50f26305e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayoutModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: DefaultLayout, + ViewPath: CategoryTemplate.Simple, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayout_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayout_ToModel.verified.txt new file mode 100644 index 000000000..8eb41339d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CategoryLayout_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: DefaultLayout, + ViewPath: CategoryTemplate.Simple, + DisplayOrder: 1, + Id: cl-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayoutModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayoutModel_ToDomain.verified.txt new file mode 100644 index 000000000..587944822 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayoutModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: CollectionDefault, + ViewPath: CollectionTemplate.Simple, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayout_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayout_ToModel.verified.txt new file mode 100644 index 000000000..f5c891988 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.CollectionLayout_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: CollectionDefault, + ViewPath: CollectionTemplate.Simple, + DisplayOrder: 1, + Id: cll-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayoutModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayoutModel_ToDomain.verified.txt new file mode 100644 index 000000000..248413138 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayoutModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: PageDefault, + ViewPath: PageTemplate.Default, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayout_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayout_ToModel.verified.txt new file mode 100644 index 000000000..688b11d7a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.PageLayout_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: PageDefault, + ViewPath: PageTemplate.Default, + DisplayOrder: 1, + Id: pgl-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayoutModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayoutModel_ToDomain.verified.txt new file mode 100644 index 000000000..31c806dac --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayoutModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: SimpleProduct, + ViewPath: ProductTemplate.Simple, + DisplayOrder: 5, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayout_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayout_ToModel.verified.txt new file mode 100644 index 000000000..abeb681e2 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.ProductLayout_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: SimpleProduct, + ViewPath: ProductTemplate.Simple, + DisplayOrder: 5, + Id: pl-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs new file mode 100644 index 000000000..99ba54da8 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs @@ -0,0 +1,108 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Domain.Pages; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Layouts; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class CatalogLayoutMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── CategoryLayout ──────────────────────────────────────────────────────── + + [TestMethod] + public Task CategoryLayout_ToModel() + { + var source = new CategoryLayout { Id = "cl-001", Name = "DefaultLayout", ViewPath = "CategoryTemplate.Simple", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CategoryLayoutModel_ToDomain() + { + var model = new CategoryLayoutModel { Name = "DefaultLayout", ViewPath = "CategoryTemplate.Simple", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── BrandLayout ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task BrandLayout_ToModel() + { + var source = new BrandLayout { Id = "bl-001", Name = "BrandDefault", ViewPath = "BrandTemplate.Simple", DisplayOrder = 2 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task BrandLayoutModel_ToDomain() + { + var model = new BrandLayoutModel { Name = "BrandDefault", ViewPath = "BrandTemplate.Simple", DisplayOrder = 2 }; + return Verify(_mapper.Map(model)); + } + + // ── CollectionLayout ────────────────────────────────────────────────────── + + [TestMethod] + public Task CollectionLayout_ToModel() + { + var source = new CollectionLayout { Id = "cll-001", Name = "CollectionDefault", ViewPath = "CollectionTemplate.Simple", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CollectionLayoutModel_ToDomain() + { + var model = new CollectionLayoutModel { Name = "CollectionDefault", ViewPath = "CollectionTemplate.Simple", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── ProductLayout ───────────────────────────────────────────────────────── + + [TestMethod] + public Task ProductLayout_ToModel() + { + var source = new ProductLayout { Id = "pl-001", Name = "SimpleProduct", ViewPath = "ProductTemplate.Simple", DisplayOrder = 5 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductLayoutModel_ToDomain() + { + var model = new ProductLayoutModel { Name = "SimpleProduct", ViewPath = "ProductTemplate.Simple", DisplayOrder = 5 }; + return Verify(_mapper.Map(model)); + } + + // ── PageLayout ──────────────────────────────────────────────────────────── + + [TestMethod] + public Task PageLayout_ToModel() + { + var source = new PageLayout { Id = "pgl-001", Name = "PageDefault", ViewPath = "PageTemplate.Default", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task PageLayoutModel_ToDomain() + { + var model = new PageLayoutModel { Name = "PageDefault", ViewPath = "PageTemplate.Default", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.BrandModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.BrandModel_ToDomain.verified.txt new file mode 100644 index 000000000..c025f8ba9 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.BrandModel_ToDomain.verified.txt @@ -0,0 +1,30 @@ +{ + Name: Apple, + Description: Apple brand, + BottomDescription: Bottom, + BrandLayoutId: layout-001, + MetaKeywords: apple, + MetaDescription: Apple products, + MetaTitle: Apple, + PictureId: pic-001, + PageSize: 20, + AllowCustomersToSelectPageSize: false, + PageSizeOptions: 20, 40, + ShowOnHomePage: true, + IncludeInMenu: true, + Icon: fa fa-apple, + DefaultSort: -1, + ExternalId: ext-brand-001, + Published: true, + DisplayOrder: 1, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001, + store-002 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Brand_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Brand_ToModel.verified.txt new file mode 100644 index 000000000..199e43c94 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Brand_ToModel.verified.txt @@ -0,0 +1,22 @@ +{ + Name: Apple, + Description: Apple brand, + BottomDescription: Bottom, + BrandLayoutId: layout-001, + MetaKeywords: apple, + MetaDescription: Apple products, + MetaTitle: Apple, + SeName: apple, + PictureId: pic-001, + PageSize: 20, + AllowCustomersToSelectPageSize: false, + PageSizeOptions: 20, 40, + ShowOnHomePage: true, + IncludeInMenu: true, + Icon: fa fa-apple, + DefaultSort: -1, + Published: true, + DisplayOrder: 1, + ExternalId: ext-brand-001, + Id: brand-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CategoryModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CategoryModel_ToDomain.verified.txt new file mode 100644 index 000000000..0244f4308 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CategoryModel_ToDomain.verified.txt @@ -0,0 +1,36 @@ +{ + Name: Electronics, + Description: Electronic products, + BottomDescription: More electronics, + CategoryLayoutId: layout-001, + MetaKeywords: electronics, gadgets, + MetaDescription: Shop electronics online, + MetaTitle: Electronics, + ParentCategoryId: parent-001, + PictureId: pic-001, + PageSize: 15, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 15, 30, 60, + ShowOnHomePage: true, + FeaturedProductsOnHomePage: false, + ShowOnSearchBox: true, + SearchBoxDisplayOrder: 2, + IncludeInMenu: true, + Published: true, + DisplayOrder: 5, + ExternalId: ext-001, + Flag: New, + FlagStyle: bg-success, + Icon: fa fa-laptop, + HideOnCatalog: false, + LimitedToGroups: true, + CustomerGroups: [ + grp-001, + grp-002 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Category_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Category_ToModel.verified.txt new file mode 100644 index 000000000..9283c92e5 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Category_ToModel.verified.txt @@ -0,0 +1,28 @@ +{ + Name: Electronics, + Description: Electronic products, + BottomDescription: More electronics, + CategoryLayoutId: layout-001, + MetaKeywords: electronics, gadgets, + MetaDescription: Shop electronics online, + MetaTitle: Electronics, + SeName: electronics, + ParentCategoryId: parent-001, + PictureId: pic-001, + PageSize: 15, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 15, 30, 60, + ShowOnHomePage: true, + FeaturedProductsOnHomePage: false, + IncludeInMenu: true, + Published: true, + DisplayOrder: 5, + ExternalId: ext-001, + Flag: New, + FlagStyle: bg-success, + Icon: fa fa-laptop, + HideOnCatalog: false, + ShowOnSearchBox: true, + SearchBoxDisplayOrder: 2, + Id: cat-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CollectionModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CollectionModel_ToDomain.verified.txt new file mode 100644 index 000000000..fd1136589 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.CollectionModel_ToDomain.verified.txt @@ -0,0 +1,18 @@ +{ + Name: Summer Sale, + Description: Summer collection, + CollectionLayoutId: layout-002, + PageSize: 5, + AllowCustomersToSelectPageSize: false, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + IncludeInMenu: false, + Published: true, + DisplayOrder: 3, + LimitedToGroups: false, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Collection_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Collection_ToModel.verified.txt new file mode 100644 index 000000000..12f4e8dcb --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Collection_ToModel.verified.txt @@ -0,0 +1,22 @@ +{ + Name: Summer Sale, + Description: Summer collection, + BottomDescription: End of summer, + CollectionLayoutId: layout-002, + MetaKeywords: summer, sale, + MetaDescription: Summer sales, + MetaTitle: Summer Collection, + SeName: summer-sale, + PictureId: pic-002, + PageSize: 10, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 10, 20, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + IncludeInMenu: false, + DefaultSort: -1, + Published: true, + DisplayOrder: 3, + ExternalId: ext-col-001, + Id: col-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDateModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDateModel_ToDomain.verified.txt new file mode 100644 index 000000000..a44118b17 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDateModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: 2-3 Business Days, + DisplayOrder: 1, + ColorSquaresRgb: #00FF00, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDate_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDate_ToModel.verified.txt new file mode 100644 index 000000000..4751e7f6c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DeliveryDate_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: 2-3 Business Days, + DisplayOrder: 1, + ColorSquaresRgb: #00FF00, + Id: dd-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DiscountModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DiscountModel_ToDomain.verified.txt new file mode 100644 index 000000000..230194450 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.DiscountModel_ToDomain.verified.txt @@ -0,0 +1,19 @@ +{ + Name: 10% Off, + DiscountTypeId: AssignedToOrderTotal, + UsePercentage: true, + DiscountPercentage: 10.0, + CalculateByPlugin: false, + MaximumDiscountAmount: 0.0, + RequiresCouponCode: true, + Reused: false, + IsCumulative: false, + MaximumDiscountedQuantity: 0, + IsEnabled: true, + LimitedToStores: true, + Stores: [ + store-001, + store-002 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel.verified.txt new file mode 100644 index 000000000..26f0ecace --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel.verified.txt @@ -0,0 +1,19 @@ +{ + Name: 10% Off, + DiscountTypeId: 1, + UsePercentage: true, + DiscountPercentage: 10.0, + CurrencyCode: USD, + CalculateByPlugin: false, + MaximumDiscountAmount: 100.0, + RequiresCouponCode: true, + Reused: false, + IsCumulative: false, + LimitationTimes: 100, + MaximumDiscountedQuantity: 5, + IsEnabled: true, + Stores: [ + store-001 + ], + Id: disc-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.GiftVoucher_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.GiftVoucher_ToModel.verified.txt new file mode 100644 index 000000000..86d4228db --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.GiftVoucher_ToModel.verified.txt @@ -0,0 +1,13 @@ +{ + Amount: 50.0, + CurrencyCode: USD, + IsGiftVoucherActivated: true, + Code: GIFT50, + RecipientName: John Doe, + RecipientEmail: john@example.com, + SenderName: Jane, + SenderEmail: jane@example.com, + Message: Happy Birthday!, + IsRecipientNotified: false, + Id: gv-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs new file mode 100644 index 000000000..acb07ad29 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs @@ -0,0 +1,310 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Domain.Discounts; +using Grand.Domain.Orders; +using Grand.Domain.Shipping; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Catalog; +using Grand.Web.AdminShared.Models.Discounts; +using Grand.Web.AdminShared.Models.Orders; +using Grand.Web.AdminShared.Models.Shipping; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class CatalogMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── Category ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Category_ToModel() + { + var source = new Category { + Id = "cat-001", + Name = "Electronics", + Description = "Electronic products", + BottomDescription = "More electronics", + CategoryLayoutId = "layout-001", + MetaKeywords = "electronics, gadgets", + MetaDescription = "Shop electronics online", + MetaTitle = "Electronics", + ParentCategoryId = "parent-001", + PictureId = "pic-001", + PageSize = 15, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "15, 30, 60", + ShowOnHomePage = true, + FeaturedProductsOnHomePage = false, + ShowOnSearchBox = true, + SearchBoxDisplayOrder = 2, + IncludeInMenu = true, + Published = true, + DisplayOrder = 5, + ExternalId = "ext-001", + Flag = "New", + FlagStyle = "bg-success", + Icon = "fa fa-laptop", + HideOnCatalog = false, + DefaultSort = 0, + SeName = "electronics" + }; + var result = _mapper.Map(source); + return Verify(result); + } + + [TestMethod] + public Task CategoryModel_ToDomain() + { + var model = new CategoryModel { + Id = "cat-001", + Name = "Electronics", + Description = "Electronic products", + BottomDescription = "More electronics", + CategoryLayoutId = "layout-001", + MetaKeywords = "electronics, gadgets", + MetaDescription = "Shop electronics online", + MetaTitle = "Electronics", + ParentCategoryId = "parent-001", + PictureId = "pic-001", + PageSize = 15, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "15, 30, 60", + ShowOnHomePage = true, + FeaturedProductsOnHomePage = false, + ShowOnSearchBox = true, + SearchBoxDisplayOrder = 2, + IncludeInMenu = true, + Published = true, + DisplayOrder = 5, + ExternalId = "ext-001", + Flag = "New", + FlagStyle = "bg-success", + Icon = "fa fa-laptop", + HideOnCatalog = false, + DefaultSort = 0, + CustomerGroups = ["grp-001", "grp-002"], + Stores = ["store-001"] + }; + var result = _mapper.Map(model); + return Verify(result); + } + + // ── Brand ───────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Brand_ToModel() + { + var source = new Brand { + Id = "brand-001", + Name = "Apple", + Description = "Apple brand", + BottomDescription = "Bottom", + BrandLayoutId = "layout-001", + MetaKeywords = "apple", + MetaDescription = "Apple products", + MetaTitle = "Apple", + PictureId = "pic-001", + PageSize = 20, + AllowCustomersToSelectPageSize = false, + PageSizeOptions = "20, 40", + ShowOnHomePage = true, + IncludeInMenu = true, + Icon = "fa fa-apple", + DefaultSort = -1, + Published = true, + DisplayOrder = 1, + ExternalId = "ext-brand-001", + SeName = "apple" + }; + var result = _mapper.Map(source); + return Verify(result); + } + + [TestMethod] + public Task BrandModel_ToDomain() + { + var model = new BrandModel { + Id = "brand-001", + Name = "Apple", + Description = "Apple brand", + BottomDescription = "Bottom", + BrandLayoutId = "layout-001", + MetaKeywords = "apple", + MetaDescription = "Apple products", + MetaTitle = "Apple", + PictureId = "pic-001", + PageSize = 20, + AllowCustomersToSelectPageSize = false, + PageSizeOptions = "20, 40", + ShowOnHomePage = true, + IncludeInMenu = true, + Icon = "fa fa-apple", + DefaultSort = -1, + Published = true, + DisplayOrder = 1, + ExternalId = "ext-brand-001", + CustomerGroups = ["grp-001"], + Stores = ["store-001", "store-002"] + }; + var result = _mapper.Map(model); + return Verify(result); + } + + // ── Collection ──────────────────────────────────────────────────────────── + + [TestMethod] + public Task Collection_ToModel() + { + var source = new Collection { + Id = "col-001", + Name = "Summer Sale", + Description = "Summer collection", + BottomDescription = "End of summer", + CollectionLayoutId = "layout-002", + MetaKeywords = "summer, sale", + MetaDescription = "Summer sales", + MetaTitle = "Summer Collection", + PictureId = "pic-002", + PageSize = 10, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "10, 20", + ShowOnHomePage = false, + IncludeInMenu = false, + Published = true, + DisplayOrder = 3, + ExternalId = "ext-col-001", + SeName = "summer-sale" + }; + var result = _mapper.Map(source); + return Verify(result); + } + + [TestMethod] + public Task CollectionModel_ToDomain() + { + var model = new CollectionModel { + Id = "col-001", + Name = "Summer Sale", + Description = "Summer collection", + CollectionLayoutId = "layout-002", + Published = true, + DisplayOrder = 3, + Stores = ["store-001"] + }; + var result = _mapper.Map(model); + return Verify(result); + } + + // ── DeliveryDate ────────────────────────────────────────────────────────── + + [TestMethod] + public Task DeliveryDate_ToModel() + { + var source = new DeliveryDate { + Id = "dd-001", + Name = "2-3 Business Days", + DisplayOrder = 1, + ColorSquaresRgb = "#00FF00" + }; + var result = _mapper.Map(source); + return Verify(result); + } + + [TestMethod] + public Task DeliveryDateModel_ToDomain() + { + var model = new DeliveryDateModel { + Name = "2-3 Business Days", + DisplayOrder = 1, + ColorSquaresRgb = "#00FF00" + }; + var result = _mapper.Map(model); + return Verify(result); + } + + // ── Discount ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Discount_ToModel() + { + var source = new Discount { + Id = "disc-001", + Name = "10% Off", + DiscountTypeId = (DiscountType)1, + UsePercentage = true, + DiscountPercentage = 10, + DiscountAmount = 0, + MaximumDiscountAmount = 100, + StartDateUtc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc), + EndDateUtc = new DateTime(2024, 12, 31, 0, 0, 0, DateTimeKind.Utc), + RequiresCouponCode = true, + IsEnabled = true, + LimitationTimes = 100, + MaximumDiscountedQuantity = 5, + CurrencyCode = "USD", + Stores = ["store-001"] + }; + var result = _mapper.Map(source); + return Verify(result); + } + + [TestMethod] + public Task DiscountModel_ToDomain() + { + var model = new DiscountModel { + Id = "disc-001", + Name = "10% Off", + DiscountTypeId = 1, + UsePercentage = true, + DiscountPercentage = 10, + DiscountAmount = 0, + RequiresCouponCode = true, + IsEnabled = true, + Stores = ["store-001", "store-002"] + }; + var result = _mapper.Map(model); + return Verify(result); + } + + // ── GiftVoucher ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task GiftVoucher_ToModel() + { + var source = new GiftVoucher { + Id = "gv-001", + GiftVoucherTypeId = 0, + Amount = 50.00, + IsGiftVoucherActivated = true, + Code = "GIFT50", + RecipientName = "John Doe", + RecipientEmail = "john@example.com", + SenderName = "Jane", + SenderEmail = "jane@example.com", + Message = "Happy Birthday!", + IsRecipientNotified = false, + CreatedOnUtc = new DateTime(2024, 6, 1, 10, 0, 0, DateTimeKind.Utc), + CurrencyCode = "USD" + }; + var result = _mapper.Map(source); + return Verify(result); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValueModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValueModel_ToDomain.verified.txt new file mode 100644 index 000000000..bff6bdc2d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValueModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + Name: Red, + PriceAdjustment: 5.0, + WeightAdjustment: 0.1, + Cost: 1.5, + IsPreSelected: true, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToModel.verified.txt new file mode 100644 index 000000000..b1b257b32 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Name: Red, + PriceAdjustment: 5.0, + PriceAdjustmentStr: 5.00, + WeightAdjustment: 0.1, + WeightAdjustmentStr: 0.10, + Cost: 1.5, + IsPreSelected: true, + DisplayOrder: 1, + Id: ppav-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToProductAttributeValue.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToProductAttributeValue.verified.txt new file mode 100644 index 000000000..dd52bf8f8 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.PredefinedAttributeValue_ToProductAttributeValue.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Blue, + PriceAdjustment: 3.0, + Cost: 1.0, + IsPreSelected: false, + DisplayOrder: 2, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombinationModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombinationModel_ToDomain.verified.txt new file mode 100644 index 000000000..63c526d18 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombinationModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + StockQuantity: 10, + AllowOutOfStockOrders: false, + Sku: SKU-RED-M, + Gtin: 1234567890, + OverriddenPrice: 95.0, + NotifyAdminForQuantityBelow: 2, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt new file mode 100644 index 000000000..42831bb60 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Id: pac-001, + StockQuantity: 10, + AllowOutOfStockOrders: false, + Sku: SKU-RED-M, + Gtin: 1234567890, + OverriddenPrice: 95.0, + NotifyAdminForQuantityBelow: 2, + Attributes: System.Collections.Generic.List`1[Grand.Domain.Common.CustomAttribute], + UseMultipleWarehouses: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMappingModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMappingModel_ToDomain.verified.txt new file mode 100644 index 000000000..a8c8ebe5d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMappingModel_ToDomain.verified.txt @@ -0,0 +1,10 @@ +{ + ProductAttributeId: pa-001, + TextPrompt: Choose color, + IsRequired: true, + ShowOnCatalogPage: false, + Combination: false, + AttributeControlTypeId: DropdownList, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMapping_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMapping_ToModel.verified.txt new file mode 100644 index 000000000..1a7d15d0e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeMapping_ToModel.verified.txt @@ -0,0 +1,13 @@ +{ + ProductAttributeId: pa-001, + TextPrompt: Choose color, + IsRequired: true, + ShowOnCatalogPage: false, + AttributeControlTypeId: DropdownList, + DisplayOrder: 1, + Combination: false, + ShouldHaveValues: false, + ValidationRulesAllowed: false, + ConditionAllowed: false, + Id: pam-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeModel_ToDomain.verified.txt new file mode 100644 index 000000000..92ddee524 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + Name: Color, + Description: Product color, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttribute_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttribute_ToModel.verified.txt new file mode 100644 index 000000000..4be5efdaf --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttribute_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Color, + Description: Product color, + Id: pa-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain.verified.txt new file mode 100644 index 000000000..c7924c187 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain.verified.txt @@ -0,0 +1,51 @@ +{ + VisibleIndividually: true, + Name: Laptop Pro, + ShortDescription: High-performance laptop, + FullDescription: Full description, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + Sku: LAPTOP-001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: false, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockQuantity: 50, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 999.99, + OldPrice: 1199.99, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + Weight: 2.5, + Length: 35.0, + Width: 24.0, + Height: 2.0, + AuctionEnded: false, + DisplayOrder: 1, + Published: true, + LimitedToGroups: false, + SeName: laptop-pro, + LimitedToStores: false, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductReviewModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductReviewModel_ToDomain.verified.txt new file mode 100644 index 000000000..8ddeffef3 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductReviewModel_ToDomain.verified.txt @@ -0,0 +1,11 @@ +{ + CustomerId: cust-001, + ProductId: prod-001, + IsApproved: true, + Title: Great product, + ReviewText: Really impressed!, + ReplyText: Thank you!, + ConfirmedPurchase: false, + Rating: 5, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecificationModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecificationModel_ToDomain.verified.txt new file mode 100644 index 000000000..1d78cb2ed --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecificationModel_ToDomain.verified.txt @@ -0,0 +1,10 @@ +{ + AttributeTypeId: 1, + SpecificationAttributeId: sa-001, + SpecificationAttributeOptionId: sao-001, + CustomValue: Custom, + AllowFiltering: true, + ShowOnProductPage: true, + DisplayOrder: 2, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecification_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecification_ToModel.verified.txt new file mode 100644 index 000000000..7b38f931e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductSpecification_ToModel.verified.txt @@ -0,0 +1,10 @@ +{ + Id: psa-001, + SpecificationAttributeId: sa-001, + AttributeTypeId: 1, + SpecificationAttributeOptionId: sao-001, + CustomValue: Custom, + AllowFiltering: true, + ShowOnProductPage: true, + DisplayOrder: 2 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel.verified.txt new file mode 100644 index 000000000..c57b50cc9 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel.verified.txt @@ -0,0 +1,76 @@ +{ + Id: prod-001, + AuctionEnded: false, + VisibleIndividually: true, + ProductLayoutId: pl-001, + Name: Laptop Pro, + ShortDescription: High-performance laptop, + FullDescription: A full description of the laptop, + AdminComment: Internal note, + BrandId: brand-001, + VendorId: vendor-001, + ShowOnHomePage: true, + BestSeller: false, + MetaKeywords: laptop, + MetaDescription: Buy laptop, + MetaTitle: Laptop, + SeName: laptop-pro, + AllowCustomerReviews: true, + Sku: LAPTOP-001, + Gtin: 1234567890, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + CalendarModel: { + Interval: 1, + IncBothDate: false, + Quantity: 1, + Monday: false, + Tuesday: false, + Wednesday: false, + Thursday: false, + Friday: false, + Saturday: false, + Sunday: false + }, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + ManageInventoryMethodId: 1, + UseMultipleWarehouses: false, + StockQuantity: 50, + StockAvailability: false, + DisplayStockQuantity: true, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 999.99, + OldPrice: 1199.99, + ProductCost: 600.0, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + Weight: 2.5, + Length: 35.0, + Width: 24.0, + Height: 2.0, + DisplayOrder: 1, + Published: true, + AddPictureModel: { + IsDefault: false + }, + CopyProductModel: { + CopyImages: false, + Published: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeModel_ToDomain.verified.txt new file mode 100644 index 000000000..25686e374 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + Name: Screen Size, + DisplayOrder: 1, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOptionModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOptionModel_ToDomain.verified.txt new file mode 100644 index 000000000..5bed76332 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOptionModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: 15 inch, + ColorSquaresRgb: #FFFFFF, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOption_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOption_ToModel.verified.txt new file mode 100644 index 000000000..46d52833e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttributeOption_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Name: 15 inch, + SeName: 15-inch, + ColorSquaresRgb: #FFFFFF, + EnableColorSquaresRgb: false, + DisplayOrder: 1, + Id: sao-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttribute_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttribute_ToModel.verified.txt new file mode 100644 index 000000000..8abbb8e88 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.SpecificationAttribute_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Screen Size, + SeName: screen-size, + DisplayOrder: 1, + Id: sattr-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPriceModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPriceModel_ToDomain.verified.txt new file mode 100644 index 000000000..123f9ea59 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPriceModel_ToDomain.verified.txt @@ -0,0 +1,8 @@ +{ + StoreId: store-001, + CustomerGroupId: grp-001, + CurrencyCode: USD, + Quantity: 10, + Price: 89.99, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPrice_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPrice_ToModel.verified.txt new file mode 100644 index 000000000..4cfdfb247 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.TierPrice_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + CustomerGroupId: grp-001, + StoreId: store-001, + CurrencyCode: USD, + Quantity: 10, + Price: 89.99, + Id: tp-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs new file mode 100644 index 000000000..a3127659e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs @@ -0,0 +1,357 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Catalog; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class CatalogProductMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── Product ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Product_ToModel() + { + var source = new Product { + Id = "prod-001", + Name = "Laptop Pro", + ShortDescription = "High-performance laptop", + FullDescription = "A full description of the laptop", + AdminComment = "Internal note", + ProductLayoutId = "pl-001", + BrandId = "brand-001", + VendorId = "vendor-001", + ShowOnHomePage = true, + MetaKeywords = "laptop", + MetaDescription = "Buy laptop", + MetaTitle = "Laptop", + AllowCustomerReviews = true, + Published = true, + VisibleIndividually = true, + Price = 999.99, + OldPrice = 1199.99, + ProductCost = 600, + MarkAsNew = false, + Weight = 2.5, + Length = 35, + Width = 24, + Height = 2, + DisplayOrder = 1, + DisplayStockQuantity = true, + ManageInventoryMethodId = (ManageInventoryMethod)1, + StockQuantity = 50, + Sku = "LAPTOP-001", + Gtin = "1234567890", + SeName = "laptop-pro" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductModel_ToDomain() + { + var model = new ProductModel { + Id = "prod-001", + Name = "Laptop Pro", + ShortDescription = "High-performance laptop", + FullDescription = "Full description", + Published = true, + VisibleIndividually = true, + Price = 999.99, + OldPrice = 1199.99, + Sku = "LAPTOP-001", + Weight = 2.5, + Length = 35, + Width = 24, + Height = 2, + StockQuantity = 50, + DisplayOrder = 1, + SeName = "laptop-pro" + }; + return Verify(_mapper.Map(model)); + } + + // ── TierPrice ───────────────────────────────────────────────────────────── + + [TestMethod] + public Task TierPrice_ToModel() + { + var source = new TierPrice { + Id = "tp-001", + StoreId = "store-001", + CustomerGroupId = "grp-001", + Quantity = 10, + Price = 89.99, + StartDateTimeUtc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc), + EndDateTimeUtc = new DateTime(2024, 12, 31, 0, 0, 0, DateTimeKind.Utc), + CurrencyCode = "USD" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task TierPriceModel_ToDomain() + { + var model = new ProductModel.TierPriceModel { + StoreId = "store-001", + CustomerGroupId = "grp-001", + Quantity = 10, + Price = 89.99, + CurrencyCode = "USD" + }; + return Verify(_mapper.Map(model)); + } + + // ── ProductSpecificationAttribute ───────────────────────────────────────── + + [TestMethod] + public Task ProductSpecification_ToModel() + { + var source = new ProductSpecificationAttribute { + Id = "psa-001", + AttributeTypeId = (SpecificationAttributeType)1, + SpecificationAttributeId = "sa-001", + SpecificationAttributeOptionId = "sao-001", + CustomValue = "Custom", + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductSpecificationModel_ToDomain() + { + var model = new ProductModel.AddProductSpecificationAttributeModel { + AttributeTypeId = (SpecificationAttributeType)1, + SpecificationAttributeId = "sa-001", + SpecificationAttributeOptionId = "sao-001", + CustomValue = "Custom", + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2 + }; + return Verify(_mapper.Map(model)); + } + + // ── ProductAttribute ────────────────────────────────────────────────────── + + [TestMethod] + public Task ProductAttribute_ToModel() + { + var source = new ProductAttribute { + Id = "pa-001", + Name = "Color", + Description = "Product color" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductAttributeModel_ToDomain() + { + var model = new ProductAttributeModel { + Name = "Color", + Description = "Product color", + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── PredefinedProductAttributeValue ─────────────────────────────────────── + + [TestMethod] + public Task PredefinedAttributeValue_ToModel() + { + var source = new PredefinedProductAttributeValue { + Id = "ppav-001", + Name = "Red", + PriceAdjustment = 5.00, + WeightAdjustment = 0.1, + Cost = 1.5, + IsPreSelected = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task PredefinedAttributeValueModel_ToDomain() + { + var model = new PredefinedProductAttributeValueModel { + Name = "Red", + PriceAdjustment = 5.00, + WeightAdjustment = 0.1, + Cost = 1.5, + IsPreSelected = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── ProductAttributeMapping ─────────────────────────────────────────────── + + [TestMethod] + public Task ProductAttributeMapping_ToModel() + { + var source = new ProductAttributeMapping { + Id = "pam-001", + ProductAttributeId = "pa-001", + TextPrompt = "Choose color", + IsRequired = true, + AttributeControlTypeId = (AttributeControlType)1, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductAttributeMappingModel_ToDomain() + { + var model = new ProductModel.ProductAttributeMappingModel { + ProductAttributeId = "pa-001", + TextPrompt = "Choose color", + IsRequired = true, + AttributeControlTypeId = (AttributeControlType)1, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── ProductAttributeCombination ─────────────────────────────────────────── + + [TestMethod] + public Task ProductAttributeCombination_ToModel() + { + var source = new ProductAttributeCombination { + Id = "pac-001", + Sku = "SKU-RED-M", + Gtin = "1234567890", + AllowOutOfStockOrders = false, + StockQuantity = 10, + OverriddenPrice = 95.00, + NotifyAdminForQuantityBelow = 2 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductAttributeCombinationModel_ToDomain() + { + var model = new ProductAttributeCombinationModel { + Sku = "SKU-RED-M", + Gtin = "1234567890", + AllowOutOfStockOrders = false, + StockQuantity = 10, + OverriddenPrice = 95.00, + NotifyAdminForQuantityBelow = 2 + }; + return Verify(_mapper.Map(model)); + } + + // ── PredefinedAttributeValue → ProductAttributeValue ───────────────────── + + [TestMethod] + public Task PredefinedAttributeValue_ToProductAttributeValue() + { + var source = new PredefinedProductAttributeValue { + Id = "ppav-001", + Name = "Blue", + PriceAdjustment = 3.00, + WeightAdjustment = 0, + Cost = 1.0, + IsPreSelected = false, + DisplayOrder = 2 + }; + return Verify(_mapper.Map(source)); + } + + // ── ProductReview (model → domain) ──────────────────────────────────────── + + [TestMethod] + public Task ProductReviewModel_ToDomain() + { + var model = new ProductReviewModel { + CustomerId = "cust-001", + ProductId = "prod-001", + IsApproved = true, + Title = "Great product", + ReviewText = "Really impressed!", + ReplyText = "Thank you!", + Rating = 5 + }; + return Verify(_mapper.Map(model)); + } + + // ── SpecificationAttribute ──────────────────────────────────────────────── + + [TestMethod] + public Task SpecificationAttribute_ToModel() + { + var source = new SpecificationAttribute { + Id = "sattr-001", + Name = "Screen Size", + DisplayOrder = 1, + SeName = "screen-size" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task SpecificationAttributeModel_ToDomain() + { + var model = new SpecificationAttributeModel { + Name = "Screen Size", + DisplayOrder = 1, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task SpecificationAttributeOption_ToModel() + { + var source = new SpecificationAttributeOption { + Id = "sao-001", + Name = "15 inch", + DisplayOrder = 1, + ColorSquaresRgb = "#FFFFFF", + SeName = "15-inch" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task SpecificationAttributeOptionModel_ToDomain() + { + var model = new SpecificationAttributeOptionModel { + Name = "15 inch", + DisplayOrder = 1, + ColorSquaresRgb = "#FFFFFF" + }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeModel_ToDomain.verified.txt new file mode 100644 index 000000000..146d5dde0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeModel_ToDomain.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Custom Field, + IsRequired: true, + AttributeControlTypeId: 1, + DisplayOrder: 1, + AttributeControlType: DropdownList, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValueModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValueModel_ToDomain.verified.txt new file mode 100644 index 000000000..cbd60ef10 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValueModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Option A, + IsPreSelected: true, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValue_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValue_ToModel.verified.txt new file mode 100644 index 000000000..92d369196 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttributeValue_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Option A, + IsPreSelected: true, + DisplayOrder: 1, + Id: aav-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttribute_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttribute_ToModel.verified.txt new file mode 100644 index 000000000..23f3a0fe5 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressAttribute_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Custom Field, + IsRequired: true, + AttributeControlTypeId: 1, + DisplayOrder: 1, + Id: aa-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressModel_ToDomain.verified.txt new file mode 100644 index 000000000..9aa6044f1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.AddressModel_ToDomain.verified.txt @@ -0,0 +1,14 @@ +{ + FirstName: John, + LastName: Doe, + Email: john@example.com, + Company: ACME Corp, + CountryId: country-001, + StateProvinceId: state-001, + City: New York, + Address1: 123 Main St, + Address2: Apt 4B, + ZipPostalCode: 10001, + PhoneNumber: +1-555-1234, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Address_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Address_ToModel.verified.txt new file mode 100644 index 000000000..db06a72f7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Address_ToModel.verified.txt @@ -0,0 +1,42 @@ +{ + FirstName: John, + LastName: Doe, + Email: john@example.com, + Company: ACME Corp, + CountryId: country-001, + StateProvinceId: state-001, + City: New York, + Address1: 123 Main St, + Address2: Apt 4B, + ZipPostalCode: 10001, + PhoneNumber: +1-555-1234, + FaxNumber: +1-555-5678, + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + Id: addr-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.BankAccount_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.BankAccount_ToModel.verified.txt new file mode 100644 index 000000000..08e60d0ec --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.BankAccount_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + BankCode: CHASE123, + BankName: Chase, + SwiftCode: CHASUS33, + AccountNumber: 123456789, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CountryModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CountryModel_ToDomain.verified.txt new file mode 100644 index 000000000..3c24f30ce --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CountryModel_ToDomain.verified.txt @@ -0,0 +1,16 @@ +{ + Name: United States, + AllowsBilling: true, + AllowsShipping: true, + TwoLetterIsoCode: US, + ThreeLetterIsoCode: USA, + NumericIsoCode: 840, + SubjectToVat: false, + Published: true, + DisplayOrder: 1, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Country_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Country_ToModel.verified.txt new file mode 100644 index 000000000..946805143 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Country_ToModel.verified.txt @@ -0,0 +1,13 @@ +{ + Name: United States, + AllowsBilling: true, + AllowsShipping: true, + TwoLetterIsoCode: US, + ThreeLetterIsoCode: USA, + NumericIsoCode: 840, + SubjectToVat: false, + Published: true, + DisplayOrder: 1, + NumberOfStates: 1, + Id: country-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CurrencyModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CurrencyModel_ToDomain.verified.txt new file mode 100644 index 000000000..9f1edbc2d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.CurrencyModel_ToDomain.verified.txt @@ -0,0 +1,13 @@ +{ + Name: US Dollar, + CurrencyCode: USD, + Rate: 1.0, + DisplayLocale: en-US, + Published: true, + DisplayOrder: 1, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Currency_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Currency_ToModel.verified.txt new file mode 100644 index 000000000..4f6efd9a7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Currency_ToModel.verified.txt @@ -0,0 +1,13 @@ +{ + Name: US Dollar, + CurrencyCode: USD, + DisplayLocale: en-US, + Rate: 1.0, + CustomFormatting: ${0:N2}, + NumberDecimal: 2, + Published: true, + DisplayOrder: 1, + IsPrimaryExchangeRateCurrency: false, + IsPrimaryStoreCurrency: false, + Id: cur-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentModel_ToDomain.verified.txt new file mode 100644 index 000000000..479b9b2b7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentModel_ToDomain.verified.txt @@ -0,0 +1,15 @@ +{ + Number: INV-001, + Name: Invoice #001, + Published: true, + DocumentTypeId: dt-001, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentTypeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentTypeModel_ToDomain.verified.txt new file mode 100644 index 000000000..ede2b9b71 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentTypeModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Invoice, + Description: An invoice document, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentType_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentType_ToModel.verified.txt new file mode 100644 index 000000000..6ec218b4f --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DocumentType_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Invoice, + Description: An invoice document, + DisplayOrder: 1, + Id: dt-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Document_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Document_ToModel.verified.txt new file mode 100644 index 000000000..7a0fbc849 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Document_ToModel.verified.txt @@ -0,0 +1,12 @@ +{ + Number: INV-001, + Name: Invoice #001, + Published: true, + DisplayOrder: 1, + CustomerId: cust-001, + ReferenceId: 10, + DocumentTypeId: dt-001, + CurrencyCode: USD, + TotalAmount: 500.0, + Id: doc-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHostModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHostModel_ToDomain.verified.txt new file mode 100644 index 000000000..5a573bdc3 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHostModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Url: https://example.com, + Primary: true, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHost_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHost_ToModel.verified.txt new file mode 100644 index 000000000..ecb9817b3 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.DomainHost_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Url: https://example.com, + Primary: true, + Id: dh-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.LanguageModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.LanguageModel_ToDomain.verified.txt new file mode 100644 index 000000000..bf3bf0e03 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.LanguageModel_ToDomain.verified.txt @@ -0,0 +1,14 @@ +{ + Name: English, + LanguageCulture: en-US, + UniqueSeoCode: en, + FlagImageFileName: us.png, + Rtl: false, + Published: true, + DisplayOrder: 1, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Language_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Language_ToModel.verified.txt new file mode 100644 index 000000000..73282184a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Language_ToModel.verified.txt @@ -0,0 +1,12 @@ +{ + Name: English, + LanguageCulture: en-US, + UniqueSeoCode: en, + FlagImageFileName: us.png, + Rtl: false, + DefaultCurrencyId: cur-001, + Published: true, + DisplayOrder: 1, + Search: {}, + Id: lang-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimensionModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimensionModel_ToDomain.verified.txt new file mode 100644 index 000000000..b7484f650 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimensionModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Centimeter, + SystemKeyword: cm, + Ratio: 1.0, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimension_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimension_ToModel.verified.txt new file mode 100644 index 000000000..cfbb9c902 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureDimension_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Centimeter, + SystemKeyword: cm, + Ratio: 1.0, + DisplayOrder: 1, + IsPrimaryDimension: false, + Id: md-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnitModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnitModel_ToDomain.verified.txt new file mode 100644 index 000000000..b0f4d5cde --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnitModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Piece, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnit_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnit_ToModel.verified.txt new file mode 100644 index 000000000..83c9fe2ec --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureUnit_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Piece, + DisplayOrder: 1, + Id: mu-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeightModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeightModel_ToDomain.verified.txt new file mode 100644 index 000000000..5fe102c72 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeightModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Kilogram, + SystemKeyword: kg, + Ratio: 1.0, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeight_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeight_ToModel.verified.txt new file mode 100644 index 000000000..f5e463e33 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MeasureWeight_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Kilogram, + SystemKeyword: kg, + Ratio: 1.0, + DisplayOrder: 1, + IsPrimaryWeight: false, + Id: mw-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnActionModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnActionModel_ToDomain.verified.txt new file mode 100644 index 000000000..4a3b6777a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnActionModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Refund, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnAction_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnAction_ToModel.verified.txt new file mode 100644 index 000000000..4c5f3a6d6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnAction_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Refund, + DisplayOrder: 1, + Id: mra-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReasonModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReasonModel_ToDomain.verified.txt new file mode 100644 index 000000000..5c671d38b --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReasonModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Defective, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReason_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReason_ToModel.verified.txt new file mode 100644 index 000000000..4423baa4b --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.MerchandiseReturnReason_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Defective, + DisplayOrder: 1, + Id: mrr-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatusModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatusModel_ToDomain.verified.txt new file mode 100644 index 000000000..1d84791a6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatusModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Processing, + DisplayOrder: 2, + IsSystem: false, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatus_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatus_ToModel.verified.txt new file mode 100644 index 000000000..1d1f7d885 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.OrderStatus_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Processing, + DisplayOrder: 2, + Id: os-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PermissionCreateModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PermissionCreateModel_ToDomain.verified.txt new file mode 100644 index 000000000..15f9946c7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PermissionCreateModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Manage Products, + SystemName: ManageProducts, + Area: Admin, + Category: Catalog, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Permission_ToUpdateModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Permission_ToUpdateModel.verified.txt new file mode 100644 index 000000000..542a6b199 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Permission_ToUpdateModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Manage Products, + SystemName: ManageProducts, + Area: Admin, + Category: Catalog, + Id: perm-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPointModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPointModel_ToDomain.verified.txt new file mode 100644 index 000000000..c519b9227 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPointModel_ToDomain.verified.txt @@ -0,0 +1,10 @@ +{ + Name: Main Store Pickup, + Address: { + Id: ObjectId_1 + }, + StoreId: store-001, + PickupFee: 2.0, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt new file mode 100644 index 000000000..6a1ac4936 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Name: Main Store Pickup, + Description: Pickup at main store, + AdminComment: Note, + DisplayOrder: 1, + StoreId: store-001, + PickupFee: 2.0, + Latitude: 40.7128, + Longitude: -74.006, + Id: pp-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvinceModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvinceModel_ToDomain.verified.txt new file mode 100644 index 000000000..972935134 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvinceModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: New York, + Abbreviation: NY, + Published: true, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvince_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvince_ToModel.verified.txt new file mode 100644 index 000000000..caa44e425 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StateProvince_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: New York, + Abbreviation: NY, + Published: true, + DisplayOrder: 1, + Id: sp-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt new file mode 100644 index 000000000..2fb7cfc05 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + Name: Grand Store, + Url: https://grandstore.com, + SslEnabled: true, + DefaultLanguageId: lang-001, + DisplayOrder: 1, + CompanyName: Grand LLC, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt new file mode 100644 index 000000000..37baa2633 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt @@ -0,0 +1,26 @@ +{ + Name: Grand Store, + Url: https://grandstore.com, + SslEnabled: true, + DisplayOrder: 1, + CompanyName: Grand LLC, + CompanyAddress: 123 Commerce St, + CompanyPhoneNumber: +1-555-9999, + CompanyVat: US123456, + DefaultLanguageId: lang-001, + DefaultCountryId: country-001, + DefaultCurrencyId: cur-001, + Domains: [ + { + Url: https://grandstore.com, + Primary: true, + Id: dh-001 + } + ], + BankAccount: { + BankCode: CHASUSU, + BankName: Chase, + Id: ObjectId_1 + }, + Id: store-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategoryModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategoryModel_ToDomain.verified.txt new file mode 100644 index 000000000..c7e6388b1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategoryModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Standard Rate, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategory_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategory_ToModel.verified.txt new file mode 100644 index 000000000..bda61aa29 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.TaxCategory_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Standard Rate, + DisplayOrder: 1, + Id: tc-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.WarehouseModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.WarehouseModel_ToDomain.verified.txt new file mode 100644 index 000000000..fefab2adb --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.WarehouseModel_ToDomain.verified.txt @@ -0,0 +1,10 @@ +{ + Code: WH-MAIN, + Name: Main Warehouse, + AdminComment: Primary, + Address: { + Id: ObjectId_1 + }, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Warehouse_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Warehouse_ToModel.verified.txt new file mode 100644 index 000000000..530412588 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Warehouse_ToModel.verified.txt @@ -0,0 +1,37 @@ +{ + Code: WH-MAIN, + Name: Main Warehouse, + AdminComment: Primary warehouse, + Address: { + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + Id: ObjectId_1 + }, + DisplayOrder: 1, + Id: wh-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs new file mode 100644 index 000000000..db5474d07 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs @@ -0,0 +1,594 @@ +using AutoMapper; +using Grand.Domain.Common; +using Grand.Domain.Directory; +using Grand.Domain.Documents; +using Grand.Domain.Localization; +using Grand.Domain.Orders; +using Grand.Domain.Permissions; +using Grand.Domain.Shipping; +using Grand.Domain.Stores; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Common; +using Grand.Web.AdminShared.Models.Directory; +using Grand.Web.AdminShared.Models.Documents; +using Grand.Web.AdminShared.Models.Localization; +using Grand.Web.AdminShared.Models.Orders; +using Grand.Web.AdminShared.Models.Permissions; +using Grand.Web.AdminShared.Models.Settings; +using Grand.Web.AdminShared.Models.Shipping; +using Grand.Web.AdminShared.Models.Stores; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class CommonMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── Address ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Address_ToModel() + { + var source = new Address { + Id = "addr-001", + FirstName = "John", + LastName = "Doe", + Email = "john@example.com", + Company = "ACME Corp", + CountryId = "country-001", + StateProvinceId = "state-001", + City = "New York", + Address1 = "123 Main St", + Address2 = "Apt 4B", + ZipPostalCode = "10001", + PhoneNumber = "+1-555-1234", + FaxNumber = "+1-555-5678" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task AddressModel_ToDomain() + { + var model = new AddressModel { + FirstName = "John", + LastName = "Doe", + Email = "john@example.com", + Company = "ACME Corp", + CountryId = "country-001", + StateProvinceId = "state-001", + City = "New York", + Address1 = "123 Main St", + Address2 = "Apt 4B", + ZipPostalCode = "10001", + PhoneNumber = "+1-555-1234" + }; + return Verify(_mapper.Map
(model)); + } + + // ── AddressAttribute ────────────────────────────────────────────────────── + + [TestMethod] + public Task AddressAttribute_ToModel() + { + var source = new AddressAttribute { + Id = "aa-001", + Name = "Custom Field", + IsRequired = true, + AttributeControlTypeId = 1, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task AddressAttributeModel_ToDomain() + { + var model = new AddressAttributeModel { + Name = "Custom Field", + IsRequired = true, + AttributeControlTypeId = 1, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── AddressAttributeValue ───────────────────────────────────────────────── + + [TestMethod] + public Task AddressAttributeValue_ToModel() + { + var source = new AddressAttributeValue { + Id = "aav-001", + Name = "Option A", + IsPreSelected = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task AddressAttributeValueModel_ToDomain() + { + var model = new AddressAttributeValueModel { + Name = "Option A", + IsPreSelected = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── Country ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Country_ToModel() + { + var source = new Country { + Id = "country-001", + Name = "United States", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "US", + ThreeLetterIsoCode = "USA", + NumericIsoCode = 840, + SubjectToVat = false, + Published = true, + DisplayOrder = 1, + }; + source.StateProvinces.Add(new StateProvince { Id = "sp-001", Name = "New York" }); + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CountryModel_ToDomain() + { + var model = new CountryModel { + Name = "United States", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "US", + ThreeLetterIsoCode = "USA", + NumericIsoCode = 840, + Published = true, + DisplayOrder = 1, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── StateProvince ───────────────────────────────────────────────────────── + + [TestMethod] + public Task StateProvince_ToModel() + { + var source = new StateProvince { + Id = "sp-001", + Name = "New York", + Abbreviation = "NY", + Published = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task StateProvinceModel_ToDomain() + { + var model = new StateProvinceModel { + Name = "New York", + Abbreviation = "NY", + Published = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── Currency ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Currency_ToModel() + { + var source = new Currency { + Id = "cur-001", + Name = "US Dollar", + CurrencyCode = "USD", + Rate = 1.0, + DisplayLocale = "en-US", + CustomFormatting = "${0:N2}", + Published = true, + DisplayOrder = 1, + RoundingTypeId = 0, + MidpointRoundId = 0 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CurrencyModel_ToDomain() + { + var model = new CurrencyModel { + Name = "US Dollar", + CurrencyCode = "USD", + Rate = 1.0, + DisplayLocale = "en-US", + Published = true, + DisplayOrder = 1, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── Language ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Language_ToModel() + { + var source = new Language { + Id = "lang-001", + Name = "English", + LanguageCulture = "en-US", + UniqueSeoCode = "en", + FlagImageFileName = "us.png", + Rtl = false, + Published = true, + DisplayOrder = 1, + DefaultCurrencyId = "cur-001" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task LanguageModel_ToDomain() + { + var model = new LanguageModel { + Name = "English", + LanguageCulture = "en-US", + UniqueSeoCode = "en", + FlagImageFileName = "us.png", + Rtl = false, + Published = true, + DisplayOrder = 1, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── MeasureDimension ────────────────────────────────────────────────────── + + [TestMethod] + public Task MeasureDimension_ToModel() + { + var source = new MeasureDimension { Id = "md-001", Name = "Centimeter", SystemKeyword = "cm", Ratio = 1.0, DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MeasureDimensionModel_ToDomain() + { + var model = new MeasureDimensionModel { Name = "Centimeter", SystemKeyword = "cm", Ratio = 1.0, DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── MeasureWeight ───────────────────────────────────────────────────────── + + [TestMethod] + public Task MeasureWeight_ToModel() + { + var source = new MeasureWeight { Id = "mw-001", Name = "Kilogram", SystemKeyword = "kg", Ratio = 1.0, DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MeasureWeightModel_ToDomain() + { + var model = new MeasureWeightModel { Name = "Kilogram", SystemKeyword = "kg", Ratio = 1.0, DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── MeasureUnit ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task MeasureUnit_ToModel() + { + var source = new MeasureUnit { Id = "mu-001", Name = "Piece", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MeasureUnitModel_ToDomain() + { + var model = new MeasureUnitModel { Name = "Piece", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── Store ───────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Store_ToModel() + { + var source = new Store { + Id = "store-001", + Name = "Grand Store", + Url = "https://grandstore.com", + SslEnabled = true, + CompanyName = "Grand LLC", + CompanyAddress = "123 Commerce St", + CompanyPhoneNumber = "+1-555-9999", + CompanyVat = "US123456", + DisplayOrder = 1, + DefaultLanguageId = "lang-001", + DefaultCurrencyId = "cur-001", + DefaultCountryId = "country-001", + Domains = [new DomainHost { Id = "dh-001", Url = "https://grandstore.com", Primary = true }], + BankAccount = new BankAccount { BankName = "Chase", BankCode = "CHASUSU" } + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task StoreModel_ToDomain() + { + var model = new StoreModel { + Name = "Grand Store", + Url = "https://grandstore.com", + SslEnabled = true, + CompanyName = "Grand LLC", + DefaultLanguageId = "lang-001", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task DomainHost_ToModel() + { + var source = new DomainHost { Id = "dh-001", Url = "https://example.com", Primary = true }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task DomainHostModel_ToDomain() + { + var model = new DomainHostModel { Url = "https://example.com", Primary = true }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task BankAccount_ToModel() + { + var source = new BankAccount { BankName = "Chase", BankCode = "CHASE123", SwiftCode = "CHASUS33", AccountNumber = "123456789" }; + return Verify(_mapper.Map(source)); + } + + // ── Document ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Document_ToModel() + { + var source = new Document { + Id = "doc-001", + Name = "Invoice #001", + Number = "INV-001", + DocumentTypeId = "dt-001", + CustomerId = "cust-001", + ReferenceId = Reference.Order, + StatusId = DocumentStatus.Open, + DisplayOrder = 1, + Published = true, + TotalAmount = 500, + OutstandAmount = 0, + CurrencyCode = "USD" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task DocumentModel_ToDomain() + { + var model = new DocumentModel { + Name = "Invoice #001", + Number = "INV-001", + DocumentTypeId = "dt-001", + Published = true, + CustomerGroups = ["grp-001"], + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── DocumentType ────────────────────────────────────────────────────────── + + [TestMethod] + public Task DocumentType_ToModel() + { + var source = new DocumentType { Id = "dt-001", Name = "Invoice", Description = "An invoice document", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task DocumentTypeModel_ToDomain() + { + var model = new DocumentTypeModel { Name = "Invoice", Description = "An invoice document", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── OrderStatus ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task OrderStatus_ToModel() + { + var source = new OrderStatus { Id = "os-001", Name = "Processing", DisplayOrder = 2 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task OrderStatusModel_ToDomain() + { + var model = new OrderStatusModel { Name = "Processing", DisplayOrder = 2 }; + return Verify(_mapper.Map(model)); + } + + // ── MerchandiseReturnAction ─────────────────────────────────────────────── + + [TestMethod] + public Task MerchandiseReturnAction_ToModel() + { + var source = new MerchandiseReturnAction { Id = "mra-001", Name = "Refund", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MerchandiseReturnActionModel_ToDomain() + { + var model = new MerchandiseReturnActionModel { Name = "Refund", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── MerchandiseReturnReason ─────────────────────────────────────────────── + + [TestMethod] + public Task MerchandiseReturnReason_ToModel() + { + var source = new MerchandiseReturnReason { Id = "mrr-001", Name = "Defective", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MerchandiseReturnReasonModel_ToDomain() + { + var model = new MerchandiseReturnReasonModel { Name = "Defective", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── PickupPoint ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task PickupPoint_ToModel() + { + var source = new PickupPoint { + Id = "pp-001", + Name = "Main Store Pickup", + Description = "Pickup at main store", + AdminComment = "Note", + PickupFee = 2.00, + DisplayOrder = 1, + StoreId = "store-001", + Latitude = 40.7128, + Longitude = -74.0060 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task PickupPointModel_ToDomain() + { + var model = new PickupPointModel { + Name = "Main Store Pickup", + PickupFee = 2.00, + DisplayOrder = 1, + StoreId = "store-001" + }; + return Verify(_mapper.Map(model)); + } + + // ── Warehouse ───────────────────────────────────────────────────────────── + + [TestMethod] + public Task Warehouse_ToModel() + { + var source = new Warehouse { + Id = "wh-001", + Name = "Main Warehouse", + AdminComment = "Primary warehouse", + Code = "WH-MAIN", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task WarehouseModel_ToDomain() + { + var model = new WarehouseModel { + Name = "Main Warehouse", + AdminComment = "Primary", + Code = "WH-MAIN", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── Permission ──────────────────────────────────────────────────────────── + + [TestMethod] + public Task PermissionCreateModel_ToDomain() + { + var model = new PermissionCreateModel { + Name = "Manage Products", + SystemName = "ManageProducts", + Area = "Admin", + Category = "Catalog" + }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task Permission_ToUpdateModel() + { + var source = new Permission { + Id = "perm-001", + Name = "Manage Products", + SystemName = "ManageProducts", + Area = "Admin", + Category = "Catalog" + }; + return Verify(_mapper.Map(source)); + } + + // ── TaxCategory ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task TaxCategory_ToModel() + { + var source = new Grand.Domain.Tax.TaxCategory { Id = "tc-001", Name = "Standard Rate", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task TaxCategoryModel_ToDomain() + { + var model = new Grand.Web.AdminShared.Models.Tax.TaxCategoryModel { Name = "Standard Rate", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategoryModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategoryModel_ToDomain.verified.txt new file mode 100644 index 000000000..d106fec2e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategoryModel_ToDomain.verified.txt @@ -0,0 +1,10 @@ +{ + Name: Tech News, + SeName: tech-news, + DisplayOrder: 1, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategory_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategory_ToModel.verified.txt new file mode 100644 index 000000000..32279684d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogCategory_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Tech News, + SeName: tech-news, + DisplayOrder: 1, + Id: bc-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPostModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPostModel_ToDomain.verified.txt new file mode 100644 index 000000000..7c79ce9c7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPostModel_ToDomain.verified.txt @@ -0,0 +1,12 @@ +{ + Title: First Post, + Body: Content body, + BodyOverview: Overview, + AllowComments: true, + Tags: tech,blog, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPost_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPost_ToModel.verified.txt new file mode 100644 index 000000000..99ace7a99 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogPost_ToModel.verified.txt @@ -0,0 +1,12 @@ +{ + Title: First Post, + Body: Content body, + BodyOverview: Overview, + AllowComments: true, + Tags: tech,blog, + MetaKeywords: blog, + MetaDescription: Blog post, + MetaTitle: First Post, + SeName: first-post, + Id: bp-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..c30091cb8 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettingsModel_ToDomain.verified.txt @@ -0,0 +1,8 @@ +{ + Enabled: true, + PostsPageSize: 10, + AllowNotRegisteredUsersToLeaveComments: false, + NotifyAboutNewBlogComments: false, + ShowBlogOnHomePage: false, + ShowBlogPostsInSearchAutoComplete: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettings_ToModel.verified.txt new file mode 100644 index 000000000..c30091cb8 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.BlogSettings_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Enabled: true, + PostsPageSize: 10, + AllowNotRegisteredUsersToLeaveComments: false, + NotifyAboutNewBlogComments: false, + ShowBlogOnHomePage: false, + ShowBlogPostsInSearchAutoComplete: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLessonModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLessonModel_ToDomain.verified.txt new file mode 100644 index 000000000..428f11a96 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLessonModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + Name: Introduction, + ShortDescription: Lesson overview, + DisplayOrder: 1, + CourseId: course-001, + SubjectId: subj-001, + Published: true, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLesson_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLesson_ToModel.verified.txt new file mode 100644 index 000000000..4f24f42d7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLesson_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Name: Introduction, + ShortDescription: Lesson overview, + DisplayOrder: 1, + CourseId: course-001, + SubjectId: subj-001, + VideoFile: intro.mp4, + AttachmentId: attach-001, + Published: true, + Id: lesson-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevelModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevelModel_ToDomain.verified.txt new file mode 100644 index 000000000..b8d834834 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevelModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Beginner, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevel_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevel_ToModel.verified.txt new file mode 100644 index 000000000..98dc30c12 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseLevel_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Beginner, + DisplayOrder: 1, + Id: cl-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseModel_ToDomain.verified.txt new file mode 100644 index 000000000..608d5aabe --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseModel_ToDomain.verified.txt @@ -0,0 +1,17 @@ +{ + Name: C# Fundamentals, + Description: Full course, + ShortDescription: Learn C#, + Published: true, + DisplayOrder: 1, + LevelId: cl-001, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubjectModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubjectModel_ToDomain.verified.txt new file mode 100644 index 000000000..1395e2638 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubjectModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Chapter 1, + DisplayOrder: 1, + CourseId: course-001, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubject_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubject_ToModel.verified.txt new file mode 100644 index 000000000..61c7a19d9 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.CourseSubject_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Chapter 1, + DisplayOrder: 1, + CourseId: course-001, + Id: subj-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Course_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Course_ToModel.verified.txt new file mode 100644 index 000000000..9eac962eb --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Course_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Name: C# Fundamentals, + ShortDescription: Learn C#, + Description: Full course, + DisplayOrder: 1, + Published: true, + LevelId: cl-001, + SeName: csharp-fundamentals, + ProductId: prod-001, + Id: course-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticleModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticleModel_ToDomain.verified.txt new file mode 100644 index 000000000..7d129edf8 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticleModel_ToDomain.verified.txt @@ -0,0 +1,18 @@ +{ + Content: Step-by-step guide, + DisplayOrder: 1, + Published: true, + ShowOnHomepage: false, + AllowComments: false, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Name: How to order?, + ParentCategoryId: kb-001, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticle_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticle_ToModel.verified.txt new file mode 100644 index 000000000..bc8e8f705 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseArticle_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Name: How to order?, + Content: Step-by-step guide, + ParentCategoryId: kb-001, + DisplayOrder: 1, + Published: true, + SeName: how-to-order, + ShowOnHomepage: false, + AllowComments: false, + Id: kba-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategoryModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategoryModel_ToDomain.verified.txt new file mode 100644 index 000000000..86c800b01 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategoryModel_ToDomain.verified.txt @@ -0,0 +1,15 @@ +{ + DisplayOrder: 1, + Description: Frequently asked questions, + Published: true, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Name: FAQ, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategory_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategory_ToModel.verified.txt new file mode 100644 index 000000000..bad8cb12a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseCategory_ToModel.verified.txt @@ -0,0 +1,10 @@ +{ + Name: FAQ, + ParentCategoryId: , + DisplayOrder: 1, + Description: Frequently asked questions, + Published: true, + SeName: faq, + LimitedToStores: false, + Id: kb-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..35a128db5 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettingsModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Enabled: true, + AllowNotRegisteredUsersToLeaveComments: false, + NotifyAboutNewArticleComments: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettings_ToModel.verified.txt new file mode 100644 index 000000000..35a128db5 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.KnowledgebaseSettings_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Enabled: true, + AllowNotRegisteredUsersToLeaveComments: false, + NotifyAboutNewArticleComments: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItemModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItemModel_ToDomain.verified.txt new file mode 100644 index 000000000..e3dc99439 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItemModel_ToDomain.verified.txt @@ -0,0 +1,16 @@ +{ + Title: Breaking News, + Short: Short text, + Full: Full news content, + Published: true, + AllowComments: true, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItem_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItem_ToModel.verified.txt new file mode 100644 index 000000000..988abc9ef --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsItem_ToModel.verified.txt @@ -0,0 +1,12 @@ +{ + Title: Breaking News, + Short: Short text, + Full: Full news content, + AllowComments: true, + MetaKeywords: news, + MetaDescription: News, + MetaTitle: Breaking News, + SeName: breaking-news, + Published: true, + Id: ni-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..52022629d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettingsModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Enabled: true, + AllowNotRegisteredUsersToLeaveComments: false, + NotifyAboutNewNewsComments: false, + ShowNewsOnMainPage: true, + MainPageNewsCount: 3 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettings_ToModel.verified.txt new file mode 100644 index 000000000..50c3472aa --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.NewsSettings_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Enabled: true, + AllowNotRegisteredUsersToLeaveComments: true, + NotifyAboutNewNewsComments: false, + ShowNewsOnMainPage: true, + MainPageNewsCount: 3 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.PageModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.PageModel_ToDomain.verified.txt new file mode 100644 index 000000000..d52085a2d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.PageModel_ToDomain.verified.txt @@ -0,0 +1,23 @@ +{ + SystemName: about-us, + IncludeInSitemap: true, + IncludeInMenu: false, + IncludeInFooterRow1: false, + IncludeInFooterRow2: false, + IncludeInFooterRow3: false, + DisplayOrder: 1, + AccessibleWhenStoreClosed: false, + IsPasswordProtected: false, + Title: About Us, + Body:

About us page

, + Published: true, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Page_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Page_ToModel.verified.txt new file mode 100644 index 000000000..de87b088d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.Page_ToModel.verified.txt @@ -0,0 +1,20 @@ +{ + SystemName: about-us, + IncludeInSitemap: true, + IncludeInMenu: false, + IncludeInFooterRow1: false, + IncludeInFooterRow2: false, + IncludeInFooterRow3: false, + DisplayOrder: 1, + AccessibleWhenStoreClosed: false, + IsPasswordProtected: false, + Published: true, + Title: About Us, + Body:

About us page

, + PageLayoutId: pl-001, + MetaKeywords: about, + MetaDescription: About us, + MetaTitle: About Us, + SeName: about-us, + Id: page-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs new file mode 100644 index 000000000..c75dce69d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs @@ -0,0 +1,390 @@ +using AutoMapper; +using Grand.Domain.Blogs; +using Grand.Domain.Courses; +using Grand.Domain.Knowledgebase; +using Grand.Domain.News; +using Grand.Domain.Pages; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Blogs; +using Grand.Web.AdminShared.Models.Courses; +using Grand.Web.AdminShared.Models.Knowledgebase; +using Grand.Web.AdminShared.Models.News; +using Grand.Web.AdminShared.Models.Pages; +using Grand.Web.AdminShared.Models.Settings; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class ContentMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── BlogCategory ────────────────────────────────────────────────────────── + + [TestMethod] + public Task BlogCategory_ToModel() + { + var source = new BlogCategory { + Id = "bc-001", + Name = "Tech News", + SeName = "tech-news", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task BlogCategoryModel_ToDomain() + { + var model = new BlogCategoryModel { + Name = "Tech News", + SeName = "tech-news", + DisplayOrder = 1, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── BlogPost ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task BlogPost_ToModel() + { + var source = new BlogPost { + Id = "bp-001", + Title = "First Post", + Body = "Content body", + BodyOverview = "Overview", + AllowComments = true, + Tags = "tech,blog", + StartDateUtc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc), + EndDateUtc = new DateTime(2024, 12, 31, 0, 0, 0, DateTimeKind.Utc), + MetaKeywords = "blog", + MetaDescription = "Blog post", + MetaTitle = "First Post", + SeName = "first-post" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task BlogPostModel_ToDomain() + { + var model = new BlogPostModel { + Title = "First Post", + Body = "Content body", + BodyOverview = "Overview", + AllowComments = true, + Tags = "tech,blog", + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── BlogSettings ────────────────────────────────────────────────────────── + + [TestMethod] + public Task BlogSettings_ToModel() + { + var source = new BlogSettings { Enabled = true, PostsPageSize = 10, AllowNotRegisteredUsersToLeaveComments = false }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task BlogSettingsModel_ToDomain() + { + var model = new ContentSettingsModel.BlogSettingsModel { Enabled = true, PostsPageSize = 10 }; + return Verify(_mapper.Map(model)); + } + + // ── NewsItem ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task NewsItem_ToModel() + { + var source = new NewsItem { + Id = "ni-001", + Title = "Breaking News", + Short = "Short text", + Full = "Full news content", + Published = true, + AllowComments = true, + StartDateUtc = new DateTime(2024, 6, 1, 0, 0, 0, DateTimeKind.Utc), + EndDateUtc = new DateTime(2024, 6, 30, 0, 0, 0, DateTimeKind.Utc), + MetaKeywords = "news", + MetaDescription = "News", + MetaTitle = "Breaking News", + SeName = "breaking-news" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task NewsItemModel_ToDomain() + { + var model = new NewsItemModel { + Title = "Breaking News", + Short = "Short text", + Full = "Full news content", + Published = true, + AllowComments = true, + CustomerGroups = ["grp-001"], + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── NewsSettings ────────────────────────────────────────────────────────── + + [TestMethod] + public Task NewsSettings_ToModel() + { + var source = new NewsSettings { Enabled = true, AllowNotRegisteredUsersToLeaveComments = true, NotifyAboutNewNewsComments = false, ShowNewsOnMainPage = true, MainPageNewsCount = 3 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task NewsSettingsModel_ToDomain() + { + var model = new ContentSettingsModel.NewsSettingsModel { Enabled = true, ShowNewsOnMainPage = true, MainPageNewsCount = 3 }; + return Verify(_mapper.Map(model)); + } + + // ── Page ────────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Page_ToModel() + { + var source = new Page { + Id = "page-001", + SystemName = "about-us", + IncludeInSitemap = true, + IsPasswordProtected = false, + Published = true, + Title = "About Us", + Body = "

About us page

", + MetaKeywords = "about", + MetaDescription = "About us", + MetaTitle = "About Us", + DisplayOrder = 1, + SeName = "about-us", + PageLayoutId = "pl-001" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task PageModel_ToDomain() + { + var model = new PageModel { + SystemName = "about-us", + IncludeInSitemap = true, + Published = true, + Title = "About Us", + Body = "

About us page

", + DisplayOrder = 1, + CustomerGroups = ["grp-001"], + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── KnowledgebaseCategory ───────────────────────────────────────────────── + + [TestMethod] + public Task KnowledgebaseCategory_ToModel() + { + var source = new KnowledgebaseCategory { + Id = "kb-001", + Name = "FAQ", + Description = "Frequently asked questions", + ParentCategoryId = "", + DisplayOrder = 1, + Published = true, + SeName = "faq" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task KnowledgebaseCategoryModel_ToDomain() + { + var model = new KnowledgebaseCategoryModel { + Name = "FAQ", + Description = "Frequently asked questions", + DisplayOrder = 1, + Published = true, + Stores = ["store-001"], + CustomerGroups = ["grp-001"] + }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task KnowledgebaseArticle_ToModel() + { + var source = new KnowledgebaseArticle { + Id = "kba-001", + Name = "How to order?", + Content = "Step-by-step guide", + ParentCategoryId = "kb-001", + DisplayOrder = 1, + Published = true, + SeName = "how-to-order" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task KnowledgebaseArticleModel_ToDomain() + { + var model = new KnowledgebaseArticleModel { + Name = "How to order?", + Content = "Step-by-step guide", + ParentCategoryId = "kb-001", + DisplayOrder = 1, + Published = true, + Stores = ["store-001"], + CustomerGroups = ["grp-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── KnowledgebaseSettings ───────────────────────────────────────────────── + + [TestMethod] + public Task KnowledgebaseSettings_ToModel() + { + var source = new KnowledgebaseSettings { Enabled = true, AllowNotRegisteredUsersToLeaveComments = false }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task KnowledgebaseSettingsModel_ToDomain() + { + var model = new ContentSettingsModel.KnowledgebaseSettingsModel { Enabled = true }; + return Verify(_mapper.Map(model)); + } + + // ── Course ──────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Course_ToModel() + { + var source = new Course { + Id = "course-001", + Name = "C# Fundamentals", + ShortDescription = "Learn C#", + Description = "Full course", + LevelId = "cl-001", + Published = true, + DisplayOrder = 1, + SeName = "csharp-fundamentals", + ProductId = "prod-001" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CourseModel_ToDomain() + { + var model = new CourseModel { + Name = "C# Fundamentals", + ShortDescription = "Learn C#", + Description = "Full course", + LevelId = "cl-001", + Published = true, + DisplayOrder = 1, + CustomerGroups = ["grp-001"], + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── CourseLevel ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task CourseLevel_ToModel() + { + var source = new CourseLevel { Id = "cl-001", Name = "Beginner", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CourseLevelModel_ToDomain() + { + var model = new CourseLevelModel { Name = "Beginner", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } + + // ── CourseLesson ────────────────────────────────────────────────────────── + + [TestMethod] + public Task CourseLesson_ToModel() + { + var source = new CourseLesson { + Id = "lesson-001", + Name = "Introduction", + ShortDescription = "Lesson overview", + SubjectId = "subj-001", + CourseId = "course-001", + DisplayOrder = 1, + Published = true, + AttachmentId = "attach-001", + VideoFile = "intro.mp4" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CourseLessonModel_ToDomain() + { + var model = new CourseLessonModel { + Name = "Introduction", + ShortDescription = "Lesson overview", + SubjectId = "subj-001", + CourseId = "course-001", + DisplayOrder = 1, + Published = true + }; + return Verify(_mapper.Map(model)); + } + + // ── CourseSubject ───────────────────────────────────────────────────────── + + [TestMethod] + public Task CourseSubject_ToModel() + { + var source = new CourseSubject { Id = "subj-001", Name = "Chapter 1", CourseId = "course-001", DisplayOrder = 1 }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CourseSubjectModel_ToDomain() + { + var model = new CourseSubjectModel { Name = "Chapter 1", CourseId = "course-001", DisplayOrder = 1 }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeModel_ToDomain.verified.txt new file mode 100644 index 000000000..d1cc0e0e6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeModel_ToDomain.verified.txt @@ -0,0 +1,16 @@ +{ + Name: Preferred Contact, + IsRequired: false, + AttributeControlTypeId: 1, + DisplayOrder: 1, + AttributeControlType: DropdownList, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValueModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValueModel_ToDomain.verified.txt new file mode 100644 index 000000000..f129dde24 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValueModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Email, + IsPreSelected: true, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValue_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValue_ToModel.verified.txt new file mode 100644 index 000000000..2c5c6fc04 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttributeValue_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Email, + DisplayColorSquaresRgb: false, + IsPreSelected: true, + DisplayOrder: 1, + Id: coav-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttribute_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttribute_ToModel.verified.txt new file mode 100644 index 000000000..54aec7598 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactAttribute_ToModel.verified.txt @@ -0,0 +1,9 @@ +{ + Name: Preferred Contact, + TextPrompt: How would you like to be contacted?, + IsRequired: false, + AttributeControlTypeId: 1, + DisplayOrder: 1, + ConditionAllowed: false, + Id: coa-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactUs_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactUs_ToModel.verified.txt new file mode 100644 index 000000000..3bafb5aac --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.ContactUs_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Id: cu-001, + Email: customer@example.com, + FullName: Jane Doe, + IpAddress: 192.168.1.1, + Subject: Order inquiry, + Enquiry: When will my order arrive? +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeModel_ToDomain.verified.txt new file mode 100644 index 000000000..60adbf5d0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeModel_ToDomain.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Newsletter Preference, + IsRequired: false, + IsReadOnly: false, + AttributeControlTypeId: RadioList, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValueModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValueModel_ToDomain.verified.txt new file mode 100644 index 000000000..1e042f157 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValueModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Weekly, + IsPreSelected: false, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValue_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValue_ToModel.verified.txt new file mode 100644 index 000000000..9bb22f976 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttributeValue_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Weekly, + IsPreSelected: false, + DisplayOrder: 1, + Id: cav-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttribute_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttribute_ToModel.verified.txt new file mode 100644 index 000000000..9725c2340 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerAttribute_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Newsletter Preference, + IsRequired: false, + IsReadOnly: false, + AttributeControlTypeId: 2, + DisplayOrder: 1, + Id: ca-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroupModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroupModel_ToDomain.verified.txt new file mode 100644 index 000000000..8783e58e1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroupModel_ToDomain.verified.txt @@ -0,0 +1,10 @@ +{ + Name: VIP Customers, + FreeShipping: true, + TaxExempt: false, + Active: true, + IsSystem: false, + SystemName: vip, + EnablePasswordLifetime: false, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroup_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroup_ToModel.verified.txt new file mode 100644 index 000000000..75317b7e6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerGroup_ToModel.verified.txt @@ -0,0 +1,10 @@ +{ + Name: VIP Customers, + FreeShipping: true, + TaxExempt: false, + Active: true, + IsSystem: false, + SystemName: vip, + EnablePasswordLifetime: false, + Id: cg-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTagModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTagModel_ToDomain.verified.txt new file mode 100644 index 000000000..264423ebf --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTagModel_ToDomain.verified.txt @@ -0,0 +1,4 @@ +{ + Name: BigSpender, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTag_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTag_ToModel.verified.txt new file mode 100644 index 000000000..304206b55 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.CustomerTag_ToModel.verified.txt @@ -0,0 +1,4 @@ +{ + Name: BigSpender, + Id: ct-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscriptionModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscriptionModel_ToDomain.verified.txt new file mode 100644 index 000000000..592844ad1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscriptionModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Email: subscriber@example.com, + Active: true, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt new file mode 100644 index 000000000..85961030c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Email: subscriber@example.com, + Active: true, + Categories: System.Collections.Generic.List`1[System.String], + Id: nls-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategoryModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategoryModel_ToDomain.verified.txt new file mode 100644 index 000000000..154ded9eb --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategoryModel_ToDomain.verified.txt @@ -0,0 +1,11 @@ +{ + Name: Tech Updates, + Description: Technology newsletter, + Selected: false, + DisplayOrder: 1, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategory_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategory_ToModel.verified.txt new file mode 100644 index 000000000..757872f4f --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsletterCategory_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Tech Updates, + Description: Technology newsletter, + Selected: false, + DisplayOrder: 1, + Id: nlc-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployeeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployeeModel_ToDomain.verified.txt new file mode 100644 index 000000000..82aeba7f4 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployeeModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Alice Smith, + Active: true, + Commission: 5.0, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployee_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployee_ToModel.verified.txt new file mode 100644 index 000000000..fa8e61339 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.SalesEmployee_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Alice Smith, + Active: true, + Commission: 5.0, + DisplayOrder: 1, + Id: se-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiCreateModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiCreateModel_ToDomain.verified.txt new file mode 100644 index 000000000..c91921359 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiCreateModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Email: newapi@example.com, + Password: Secret123!, + IsActive: true, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiModel_ToDomain.verified.txt new file mode 100644 index 000000000..5bd3ec495 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApiModel_ToDomain.verified.txt @@ -0,0 +1,5 @@ +{ + Email: api@example.com, + IsActive: true, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApi_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApi_ToModel.verified.txt new file mode 100644 index 000000000..41063bd76 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.UserApi_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + Email: api@example.com, + IsActive: true, + Id: ua-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs new file mode 100644 index 000000000..c2525e67d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs @@ -0,0 +1,319 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Domain.Customers; +using Grand.Domain.Messages; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Customers; +using Grand.Web.AdminShared.Models.Messages; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class CustomerMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── CustomerGroup ───────────────────────────────────────────────────────── + + [TestMethod] + public Task CustomerGroup_ToModel() + { + var source = new CustomerGroup { + Id = "cg-001", + Name = "VIP Customers", + FreeShipping = true, + TaxExempt = false, + Active = true, + IsSystem = false, + SystemName = "vip", + EnablePasswordLifetime = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CustomerGroupModel_ToDomain() + { + var model = new CustomerGroupModel { + Name = "VIP Customers", + FreeShipping = true, + TaxExempt = false, + Active = true, + SystemName = "vip" + }; + return Verify(_mapper.Map(model)); + } + + // ── CustomerAttribute ───────────────────────────────────────────────────── + + [TestMethod] + public Task CustomerAttribute_ToModel() + { + var source = new CustomerAttribute { + Id = "ca-001", + Name = "Newsletter Preference", + IsRequired = false, + AttributeControlTypeId = (AttributeControlType)2, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CustomerAttributeModel_ToDomain() + { + var model = new CustomerAttributeModel { + Name = "Newsletter Preference", + IsRequired = false, + AttributeControlTypeId = 2, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── CustomerAttributeValue ──────────────────────────────────────────────── + + [TestMethod] + public Task CustomerAttributeValue_ToModel() + { + var source = new CustomerAttributeValue { + Id = "cav-001", + Name = "Weekly", + IsPreSelected = false, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CustomerAttributeValueModel_ToDomain() + { + var model = new CustomerAttributeValueModel { + Name = "Weekly", + IsPreSelected = false, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── CustomerTag ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task CustomerTag_ToModel() + { + var source = new CustomerTag { Id = "ct-001", Name = "BigSpender" }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CustomerTagModel_ToDomain() + { + var model = new CustomerTagModel { Name = "BigSpender" }; + return Verify(_mapper.Map(model)); + } + + // ── SalesEmployee ───────────────────────────────────────────────────────── + + [TestMethod] + public Task SalesEmployee_ToModel() + { + var source = new SalesEmployee { + Id = "se-001", + Name = "Alice Smith", + Active = true, + Commission = 5.0, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task SalesEmployeeModel_ToDomain() + { + var model = new SalesEmployeeModel { + Name = "Alice Smith", + Active = true, + Commission = 5.0, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── ContactAttribute ────────────────────────────────────────────────────── + + [TestMethod] + public Task ContactAttribute_ToModel() + { + var source = new ContactAttribute { + Id = "coa-001", + Name = "Preferred Contact", + TextPrompt = "How would you like to be contacted?", + IsRequired = false, + AttributeControlTypeId = 1, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ContactAttributeModel_ToDomain() + { + var model = new ContactAttributeModel { + Name = "Preferred Contact", + IsRequired = false, + AttributeControlTypeId = 1, + DisplayOrder = 1, + CustomerGroups = ["grp-001"], + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── ContactAttributeValue ───────────────────────────────────────────────── + + [TestMethod] + public Task ContactAttributeValue_ToModel() + { + var source = new ContactAttributeValue { + Id = "coav-001", + Name = "Email", + IsPreSelected = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ContactAttributeValueModel_ToDomain() + { + var model = new ContactAttributeValueModel { + Name = "Email", + IsPreSelected = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── ContactUs ───────────────────────────────────────────────────────────── + + [TestMethod] + public Task ContactUs_ToModel() + { + var source = new ContactUs { + Id = "cu-001", + Email = "customer@example.com", + FullName = "Jane Doe", + Subject = "Order inquiry", + Enquiry = "When will my order arrive?", + EmailAccountId = "ea-001", + StoreId = "store-001", + IpAddress = "192.168.1.1" + }; + return Verify(_mapper.Map(source)); + } + + // ── NewsLetterSubscription ──────────────────────────────────────────────── + + [TestMethod] + public Task NewsLetterSubscription_ToModel() + { + var source = new NewsLetterSubscription { + Id = "nls-001", + Email = "subscriber@example.com", + Active = true, + StoreId = "store-001" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task NewsLetterSubscriptionModel_ToDomain() + { + var model = new NewsLetterSubscriptionModel { + Email = "subscriber@example.com", + Active = true + }; + return Verify(_mapper.Map(model)); + } + + // ── NewsletterCategory ──────────────────────────────────────────────────── + + [TestMethod] + public Task NewsletterCategory_ToModel() + { + var source = new NewsletterCategory { + Id = "nlc-001", + Name = "Tech Updates", + Description = "Technology newsletter", + Selected = false, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task NewsletterCategoryModel_ToDomain() + { + var model = new NewsletterCategoryModel { + Name = "Tech Updates", + Description = "Technology newsletter", + DisplayOrder = 1, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── UserApi ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task UserApi_ToModel() + { + var source = new UserApi { + Id = "ua-001", + Email = "api@example.com", + IsActive = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task UserApiModel_ToDomain() + { + var model = new UserApiModel { + Email = "api@example.com", + IsActive = true + }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task UserApiCreateModel_ToDomain() + { + var model = new UserApiCreateModel { + Email = "newapi@example.com", + Password = "Secret123!", + IsActive = true + }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ExternalAuthenticationProvider_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ExternalAuthenticationProvider_ToModel.verified.txt new file mode 100644 index 000000000..7b18bf274 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ExternalAuthenticationProvider_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + FriendlyName: Facebook authentication, + SystemName: ExternalAuth.Facebook, + IsActive: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PaymentProvider_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PaymentProvider_ToModel.verified.txt new file mode 100644 index 000000000..4d84bc146 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PaymentProvider_ToModel.verified.txt @@ -0,0 +1,10 @@ +{ + FriendlyName: Cash on Delivery, + SystemName: Payments.CashOnDelivery, + DisplayOrder: 1, + IsActive: false, + SupportCapture: false, + SupportPartiallyRefund: false, + SupportRefund: false, + SupportVoid: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PluginInfo_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PluginInfo_ToModel.verified.txt new file mode 100644 index 000000000..fc7b55cd2 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.PluginInfo_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Group: Widgets, + FriendlyName: Bootstrap Slider, + SystemName: Widgets.Slider, + Version: 2.1.2, + Author: grandnode team, + Installed: true +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ShippingRateCalculationProvider_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ShippingRateCalculationProvider_ToModel.verified.txt new file mode 100644 index 000000000..5630488f4 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.ShippingRateCalculationProvider_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + FriendlyName: Fixed Rate Shipping, + SystemName: Shipping.FixedRate, + DisplayOrder: 5, + IsActive: false, + ConfigurationUrl: ../ShippingFixedRate/Configure +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.TaxProvider_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.TaxProvider_ToModel.verified.txt new file mode 100644 index 000000000..4ace49482 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.TaxProvider_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + FriendlyName: Fixed Rate, + SystemName: Tax.FixedRate, + IsPrimaryTaxProvider: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.WidgetProvider_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.WidgetProvider_ToModel.verified.txt new file mode 100644 index 000000000..882622a95 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.WidgetProvider_ToModel.verified.txt @@ -0,0 +1,5 @@ +{ + FriendlyName: Bootstrap Slider, + SystemName: Widgets.Slider, + IsActive: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs new file mode 100644 index 000000000..ea6a4d401 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs @@ -0,0 +1,122 @@ +using AutoMapper; +using Grand.Business.Core.Interfaces.Authentication; +using Grand.Business.Core.Interfaces.Catalog.Tax; +using Grand.Business.Core.Interfaces.Checkout.Payments; +using Grand.Business.Core.Interfaces.Checkout.Shipping; +using Grand.Business.Core.Interfaces.Cms; +using Grand.Infrastructure.Plugins; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Cms; +using Grand.Web.AdminShared.Models.ExternalAuthentication; +using Grand.Web.AdminShared.Models.Payments; +using Grand.Web.AdminShared.Models.Plugins; +using Grand.Web.AdminShared.Models.Shipping; +using Grand.Web.AdminShared.Models.Tax; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class ProviderMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── PaymentMethod ───────────────────────────────────────────────────────── + + [TestMethod] + public Task PaymentProvider_ToModel() + { + var mock = new Mock(); + mock.Setup(x => x.FriendlyName).Returns("Cash on Delivery"); + mock.Setup(x => x.SystemName).Returns("Payments.CashOnDelivery"); + mock.Setup(x => x.Priority).Returns(1); + + return Verify(_mapper.Map(mock.Object)); + } + + // ── TaxProvider ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task TaxProvider_ToModel() + { + var mock = new Mock(); + mock.Setup(x => x.FriendlyName).Returns("Fixed Rate"); + mock.Setup(x => x.SystemName).Returns("Tax.FixedRate"); + + return Verify(_mapper.Map(mock.Object)); + } + + // ── ShippingRateComputationMethod ───────────────────────────────────────── + + [TestMethod] + public Task ShippingRateCalculationProvider_ToModel() + { + var mock = new Mock(); + mock.Setup(x => x.FriendlyName).Returns("Fixed Rate Shipping"); + mock.Setup(x => x.SystemName).Returns("Shipping.FixedRate"); + mock.Setup(x => x.Priority).Returns(5); + mock.Setup(x => x.ConfigurationUrl).Returns("../ShippingFixedRate/Configure"); + + return Verify(_mapper.Map(mock.Object)); + } + + // ── WidgetPlugin ────────────────────────────────────────────────────────── + + [TestMethod] + public Task WidgetProvider_ToModel() + { + var mock = new Mock(); + mock.Setup(x => x.FriendlyName).Returns("Bootstrap Slider"); + mock.Setup(x => x.SystemName).Returns("Widgets.Slider"); + mock.Setup(x => x.Priority).Returns(0); + + return Verify(_mapper.Map(mock.Object)); + } + + // ── ExternalAuthentication ──────────────────────────────────────────────── + + [TestMethod] + public Task ExternalAuthenticationProvider_ToModel() + { + var mock = new Mock(); + mock.Setup(x => x.FriendlyName).Returns("Facebook authentication"); + mock.Setup(x => x.SystemName).Returns("ExternalAuth.Facebook"); + mock.Setup(x => x.Priority).Returns(0); + + return Verify(_mapper.Map(mock.Object)); + } + + // ── PluginDescriptor ────────────────────────────────────────────────────── + + [TestMethod] + public Task PluginInfo_ToModel() + { + var pluginInfo = new PluginInfo { + FriendlyName = "Bootstrap Slider", + Group = "Widgets", + SystemName = "Widgets.Slider", + Version = "2.1.2", + Author = "grandnode team", + DisplayOrder = 0, + Installed = true + }; + + return Verify(_mapper.Map(pluginInfo)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..8fb753119 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettingsModel_ToDomain.verified.txt @@ -0,0 +1,24 @@ +{ + NameEnabled: true, + CompanyEnabled: true, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + StreetAddressEnabled: true, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + CityEnabled: false, + CityRequired: false, + CountryEnabled: true, + StateProvinceEnabled: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + DisallowUsersToChangeEmail: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettings_ToModel.verified.txt new file mode 100644 index 000000000..03b5eeb38 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AddressSettings_ToModel.verified.txt @@ -0,0 +1,24 @@ +{ + NameEnabled: true, + AddressTypeEnabled: false, + CompanyEnabled: true, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + StreetAddressEnabled: true, + StreetAddressRequired: true, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: true, + ZipPostalCodeRequired: true, + CityEnabled: true, + CityRequired: false, + CountryEnabled: true, + StateProvinceEnabled: true, + PhoneEnabled: true, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + DisallowUsersToChangeEmail: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..0e70817c9 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettingsModel_ToDomain.verified.txt @@ -0,0 +1,13 @@ +{ + SearchInProducts: true, + SearchInCategories: true, + SearchInCollections: false, + SearchInPages: false, + SearchInNews: false, + SearchInBlogs: false, + SearchInCustomers: false, + SearchInOrders: false, + MinSearchTermLength: 3, + MaxSearchResultsCount: 10, + SearchInMenu: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettings_ToModel.verified.txt new file mode 100644 index 000000000..2bcd7439e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.AdminSearchSettings_ToModel.verified.txt @@ -0,0 +1,13 @@ +{ + SearchInProducts: true, + SearchInCategories: true, + SearchInCollections: false, + SearchInPages: true, + SearchInNews: false, + SearchInBlogs: true, + SearchInCustomers: true, + SearchInOrders: true, + MinSearchTermLength: 3, + MaxSearchResultsCount: 10, + SearchInMenu: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CaptchaSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CaptchaSettings_ToModel.verified.txt new file mode 100644 index 000000000..046b57c3a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CaptchaSettings_ToModel.verified.txt @@ -0,0 +1,19 @@ +{ + CaptchaEnabled: true, + CaptchaShowOnLoginPage: true, + CaptchaShowOnRegistrationPage: false, + CaptchaShowOnPasswordRecoveryPage: false, + CaptchaShowOnContactUsPage: true, + CaptchaShowOnEmailWishlistToFriendPage: false, + CaptchaShowOnEmailProductToFriendPage: false, + CaptchaShowOnAskQuestionPage: false, + CaptchaShowOnBlogCommentPage: false, + CaptchaShowOnArticleCommentPage: false, + CaptchaShowOnNewsCommentPage: false, + CaptchaShowOnProductReviewPage: false, + CaptchaShowOnApplyVendorPage: false, + CaptchaShowOnVendorReviewPage: false, + ReCaptchaPublicKey: public-key-123, + ReCaptchaPrivateKey: private-key-456, + ReCaptchaVersion: V2 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..8116485f5 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettingsModel_ToDomain.verified.txt @@ -0,0 +1,57 @@ +{ + AllowViewUnpublishedProductPage: false, + DisplayDiscontinuedMessageForUnpublishedProducts: false, + PublishBackProductWhenCancellingOrders: false, + ShowSkuOnProductDetailsPage: true, + ShowSkuOnCatalogPages: false, + ShowSpecAttributeOnCatalogPages: false, + SecondPictureOnCatalogPages: false, + ShowMpn: false, + ShowGtin: false, + ShowFreeShippingNotification: false, + AllowProductSorting: false, + AllowProductViewModeChanging: false, + ShowProductsFromSubcategories: false, + ShowProductsFromSubcategoriesInSearchBox: false, + ShowCategoryProductNumber: false, + ShowCategoryProductNumberIncludingSubcategories: false, + CategoryBreadcrumbEnabled: false, + ShowShareButton: false, + ProductReviewsMustBeApproved: false, + AllowAnonymousUsersToReviewProduct: false, + NotifyStoreOwnerAboutNewProductReviews: false, + EmailAFriendEnabled: false, + AskQuestionOnProduct: false, + AllowAnonymousUsersToEmailAFriend: false, + ProductReviewPossibleOnlyAfterPurchasing: false, + ProductReviewPossibleOnlyOnce: false, + RecentlyViewedProductsEnabled: false, + RecommendedProductsEnabled: false, + SuggestedProductsEnabled: false, + PersonalizedProductsEnabled: false, + NewProductsEnabled: false, + NewProductsOnHomePage: false, + CompareProductsEnabled: false, + ProductSearchAutoCompleteEnabled: false, + SearchBySku: false, + SearchByDescription: false, + ShowProductImagesInSearchAutoComplete: false, + SaveSearchAutoComplete: false, + ShowBestsellersOnHomepage: false, + BestsellersFromReports: false, + SearchPageProductsPerPage: 6, + SearchPageAllowCustomersToSelectPageSize: false, + ProductsAlsoPurchasedEnabled: false, + ProductsByTagAllowCustomersToSelectPageSize: false, + IncludeShortDescriptionInCompareProducts: false, + IncludeFullDescriptionInCompareProducts: false, + IncludeFeaturedProductsInNormalLists: false, + DisplayTierPricesWithDiscounts: false, + IgnoreDiscounts: false, + IgnoreFeaturedProducts: false, + CustomerProductPrice: false, + IgnoreFilterableSpecAttributeOption: false, + IgnoreFilterableAvailableStartEndDateTime: false, + DisplayQuantityOnCatalogPages: false, + SortingByAvailability: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettings_ToModel.verified.txt new file mode 100644 index 000000000..ece1c7786 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CatalogSettings_ToModel.verified.txt @@ -0,0 +1,74 @@ +{ + AllowViewUnpublishedProductPage: false, + DisplayDiscontinuedMessageForUnpublishedProducts: true, + ShowSkuOnProductDetailsPage: true, + ShowSkuOnCatalogPages: false, + ShowMpn: false, + ShowGtin: false, + ShowFreeShippingNotification: true, + AllowProductSorting: false, + AllowProductViewModeChanging: false, + ShowProductsFromSubcategories: false, + ShowCategoryProductNumber: false, + ShowCategoryProductNumberIncludingSubcategories: false, + CategoryBreadcrumbEnabled: false, + ShowShareButton: false, + PageShareCode: , + ProductReviewsMustBeApproved: false, + AllowAnonymousUsersToReviewProduct: false, + ProductReviewPossibleOnlyAfterPurchasing: false, + ProductReviewPossibleOnlyOnce: false, + NotifyStoreOwnerAboutNewProductReviews: false, + EmailAFriendEnabled: false, + AskQuestionOnProduct: false, + AllowAnonymousUsersToEmailAFriend: false, + RecentlyViewedProductsEnabled: false, + RecommendedProductsEnabled: false, + SuggestedProductsEnabled: false, + PersonalizedProductsEnabled: false, + NewProductsEnabled: false, + NewProductsOnHomePage: false, + CompareProductsEnabled: false, + ShowBestsellersOnHomepage: false, + BestsellersFromReports: false, + SearchPageProductsPerPage: 6, + SearchPageAllowCustomersToSelectPageSize: true, + ProductSearchAutoCompleteEnabled: false, + ShowProductImagesInSearchAutoComplete: false, + ProductsAlsoPurchasedEnabled: false, + ProductsByTagAllowCustomersToSelectPageSize: false, + IncludeShortDescriptionInCompareProducts: false, + IncludeFullDescriptionInCompareProducts: false, + IgnoreDiscounts: false, + IgnoreFeaturedProducts: false, + IgnoreFilterableSpecAttributeOption: false, + IgnoreFilterableAvailableStartEndDateTime: false, + CustomerProductPrice: false, + ShowSpecAttributeOnCatalogPages: false, + SecondPictureOnCatalogPages: false, + SearchBySku: false, + SearchByDescription: false, + SaveSearchAutoComplete: false, + IncludeFeaturedProductsInNormalLists: false, + DisplayTierPricesWithDiscounts: false, + DisplayQuantityOnCatalogPages: false, + SortingByAvailability: false, + DefaultViewModes: [ + { + Disabled: false, + Selected: false, + Text: grid, + Value: grid + }, + { + Disabled: false, + Selected: false, + Text: list, + Value: list + } + ], + DefaultPageSizeOptions: 6, 3, 9, + MaxCatalogPageSize: 10, + ShowProductsFromSubcategoriesInSearchBox: false, + PublishBackProductWhenCancellingOrders: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..b6560a33d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettingsModel_ToDomain.verified.txt @@ -0,0 +1,13 @@ +{ + StoreInDatabaseContactUsForm: true, + SubjectFieldOnContactUsForm: false, + UseSystemEmailForContactUsForm: false, + SitemapEnabled: true, + SitemapIncludeCategories: false, + SitemapIncludeBrands: false, + SitemapIncludeProducts: false, + SitemapIncludeImage: false, + PopupForTermsOfServiceLinks: false, + AllowToSelectStore: false, + AllowEditProductEndedAuction: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettings_ToModel.verified.txt new file mode 100644 index 000000000..b2131de4e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CommonSettings_ToModel.verified.txt @@ -0,0 +1,12 @@ +{ + StoreInDatabaseContactUsForm: true, + SubjectFieldOnContactUsForm: false, + UseSystemEmailForContactUsForm: false, + AllowToSelectStore: false, + PopupForTermsOfServiceLinks: true, + SitemapEnabled: true, + SitemapIncludeCategories: true, + SitemapIncludeImage: false, + SitemapIncludeBrands: false, + SitemapIncludeProducts: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..90d3c60d1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettingsModel_ToDomain.verified.txt @@ -0,0 +1,50 @@ +{ + UsernamesEnabled: false, + CheckUsernameAvailabilityEnabled: false, + AllowUsersToChangeUsernames: false, + AllowUsersToChangeEmail: false, + FirstLastNameRequired: false, + DefaultPasswordFormat: Hashed, + NotifyNewCustomerRegistration: false, + HideDownloadableProductsTab: false, + HideOutOfStockSubscriptionsTab: false, + HideAuctionsTab: false, + HideNotesTab: false, + HideDocumentsTab: false, + HideSubAccountsTab: false, + DownloadableProductsValidateUser: false, + NewsletterEnabled: false, + NewsletterTickedByDefault: false, + HideNewsletterBlock: false, + NewsletterBlockAllowToUnsubscribe: false, + StoreLastVisitedPage: false, + RegistrationFreeShipping: false, + AllowUsersToDeleteAccount: false, + AllowUsersToExportData: false, + HideReviewsTab: false, + HideCoursesTab: false, + TwoFactorAuthenticationEnabled: false, + GeoEnabled: false, + GenderEnabled: false, + DateOfBirthEnabled: false, + DateOfBirthRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + CityEnabled: false, + CityRequired: false, + CountryEnabled: false, + CountryRequired: false, + StateProvinceEnabled: false, + StateProvinceRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + AcceptPrivacyPolicyEnabled: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettings_ToModel.verified.txt new file mode 100644 index 000000000..5ec100ff2 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.CustomerSettings_ToModel.verified.txt @@ -0,0 +1,48 @@ +{ + FirstLastNameRequired: false, + AllowUsersToChangeEmail: false, + UsernamesEnabled: false, + AllowUsersToChangeUsernames: true, + CheckUsernameAvailabilityEnabled: false, + GeoEnabled: false, + NotifyNewCustomerRegistration: true, + HideDownloadableProductsTab: false, + HideOutOfStockSubscriptionsTab: false, + HideAuctionsTab: false, + HideNotesTab: false, + AllowUsersToDeleteAccount: false, + AllowUsersToExportData: false, + NewsletterEnabled: true, + NewsletterTickedByDefault: false, + HideNewsletterBlock: false, + NewsletterBlockAllowToUnsubscribe: false, + RegistrationFreeShipping: false, + StoreLastVisitedPage: false, + GenderEnabled: false, + DateOfBirthEnabled: false, + DateOfBirthRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + CityEnabled: false, + CityRequired: false, + CountryEnabled: false, + CountryRequired: false, + StateProvinceEnabled: false, + StateProvinceRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + AcceptPrivacyPolicyEnabled: false, + HideReviewsTab: false, + HideCoursesTab: false, + TwoFactorAuthenticationEnabled: false, + HideSubaccountsTab: false, + HideDocumentsTab: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..64352088c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettingsModel_ToDomain.verified.txt @@ -0,0 +1,8 @@ +{ + Enabled: true, + ExchangeRate: 0.01, + PointsForRegistration: 100, + ReduceLoyaltyPointsAfterCancelOrder: false, + DisplayHowMuchWillBeEarned: false, + PointsAccumulatedForAllStores: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettings_ToModel.verified.txt new file mode 100644 index 000000000..e1e0cf82d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.LoyaltyPointsSettings_ToModel.verified.txt @@ -0,0 +1,10 @@ +{ + Enabled: true, + ExchangeRate: 0.01, + PointsForRegistration: 100, + PointsForPurchases_Amount: 10.0, + PointsForPurchases_Points: 1, + ReduceLoyaltyPointsAfterCancelOrder: false, + DisplayHowMuchWillBeEarned: false, + PointsAccumulatedForAllStores: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..4928d8549 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettingsModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + DefaultImageName: no-image.png, + ProductThumbPictureSize: 415, + DefaultPictureZoomEnabled: false, + MaximumImageSize: 1980 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettings_ToModel.verified.txt new file mode 100644 index 000000000..1af9b11e2 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.MediaSettings_ToModel.verified.txt @@ -0,0 +1,18 @@ +{ + ProductThumbPictureSize: 415, + ProductDetailsPictureSize: 550, + ProductThumbPictureSizeOnProductDetailsPage: 100, + AssociatedProductPictureSize: 220, + CategoryThumbPictureSize: 450, + BrandThumbPictureSize: 450, + CollectionThumbPictureSize: 450, + VendorThumbPictureSize: 450, + CartThumbPictureSize: 80, + MaximumImageSize: 1980, + ImageQuality: 100, + DefaultPictureZoomEnabled: false, + AutoCompleteSearchThumbPictureSize: 20, + FileManagerEnabledCommands: abort,open,file,mkdir,mkfile,parents,tmb,dim,paste,duplicate,get,rm,ls,put,size,rename,tree,resize,search,upload, + FileManagerDisabledUICommands: ping,hide,archive,extract,netmount,netunmount,zipdl, + DefaultImageName: no-image.png +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..109a0bfd8 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettingsModel_ToDomain.verified.txt @@ -0,0 +1,23 @@ +{ + IsReOrderAllowed: true, + MinOrderSubtotalAmountIncludingTax: false, + AnonymousCheckoutAllowed: true, + TermsOfServiceOnShoppingCartPage: false, + TermsOfServiceOnOrderConfirmPage: false, + DisableOrderCompletedPage: false, + AttachPdfInvoiceToOrderPlacedEmail: false, + AttachPdfInvoiceToOrderPaidEmail: false, + AttachPdfInvoiceToOrderCompletedEmail: false, + AttachPdfInvoiceToBinary: false, + MerchandiseReturnsEnabled: false, + MerchandiseReturns_AllowToSpecifyPickupAddress: false, + MerchandiseReturns_AllowToSpecifyPickupDate: false, + MerchandiseReturns_PickupDateRequired: false, + DeactivateGiftVouchersAfterCancelOrder: false, + DeactivateGiftVouchersAfterDeletingOrder: false, + GiftVouchers_Assign_StoreId: false, + CompleteOrderWhenDelivered: false, + UserCanCancelUnpaidOrder: false, + UnpublishAuctionProduct: false, + AllowCustomerToAddOrderNote: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettings_ToModel.verified.txt new file mode 100644 index 000000000..a049c0e21 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.OrderSettings_ToModel.verified.txt @@ -0,0 +1,24 @@ +{ + IsReOrderAllowed: true, + MinOrderSubtotalAmountIncludingTax: false, + AnonymousCheckoutAllowed: true, + TermsOfServiceOnShoppingCartPage: true, + TermsOfServiceOnOrderConfirmPage: false, + DisableOrderCompletedPage: false, + AttachPdfInvoiceToOrderPlacedEmail: false, + AttachPdfInvoiceToOrderPaidEmail: false, + AttachPdfInvoiceToOrderCompletedEmail: false, + MerchandiseReturnsEnabled: false, + MerchandiseReturns_AllowToSpecifyPickupAddress: false, + MerchandiseReturns_AllowToSpecifyPickupDate: false, + GiftVouchers_Activated_OrderStatusId: 30, + DeactivateGiftVouchersAfterCancelOrder: false, + DeactivateGiftVouchersAfterDeletingOrder: false, + GiftVouchers_Assign_StoreId: false, + CompleteOrderWhenDelivered: false, + UserCanCancelUnpaidOrder: false, + AllowCustomerToAddOrderNote: false, + AttachPdfInvoiceToBinary: false, + MerchandiseReturns_PickupDateRequired: false, + UnpublishAuctionProduct: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..a1bb5b191 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettingsModel_ToDomain.verified.txt @@ -0,0 +1,4 @@ +{ + Enabled: false, + AllowGuestNotifications: true +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettings_ToModel.verified.txt new file mode 100644 index 000000000..25f4aed56 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.PushNotificationsSettings_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + Enabled: false, + PushApiKey: , + SenderId: , + AuthDomain: , + DatabaseUrl: , + ProjectId: , + StorageBucket: , + AppId: , + AllowGuestNotifications: true +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SecuritySettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SecuritySettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..7b31b2539 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SecuritySettingsModel_ToDomain.verified.txt @@ -0,0 +1,18 @@ +{ + Enabled: true, + ShowOnLoginPage: true, + ShowOnRegistrationPage: false, + ShowOnPasswordRecoveryPage: false, + ShowOnContactUsPage: false, + ShowOnEmailWishlistToFriendPage: false, + ShowOnEmailProductToFriendPage: false, + ShowOnAskQuestionPage: false, + ShowOnBlogCommentPage: false, + ShowOnArticleCommentPage: false, + ShowOnNewsCommentPage: false, + ShowOnProductReviewPage: false, + ShowOnApplyVendorPage: false, + ShowOnVendorReviewPage: false, + ReCaptchaPublicKey: public-key-123, + ReCaptchaPrivateKey: private-key-456 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..e7397e384 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettingsModel_ToDomain.verified.txt @@ -0,0 +1,12 @@ +{ + PageTitleSeparator: | , + PageTitleSeoAdjustment: false, + DefaultTitle: Grand Store, + GenerateProductMetaDescription: false, + ConvertNonWesternChars: false, + AllowSlashChar: false, + AllowUnicodeCharsInUrls: true, + CanonicalUrlsEnabled: false, + TwitterMetaTags: false, + OpenGraphMetaTags: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettings_ToModel.verified.txt new file mode 100644 index 000000000..e52ac8e96 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.SeoSettings_ToModel.verified.txt @@ -0,0 +1,14 @@ +{ + PageTitleSeparator: | , + PageTitleSeoAdjustment: true, + DefaultTitle: Grand Store, + DefaultMetaKeywords: store, online, + DefaultMetaDescription: Best online store, + GenerateProductMetaDescription: true, + ConvertNonWesternChars: false, + CanonicalUrlsEnabled: false, + TwitterMetaTags: false, + OpenGraphMetaTags: false, + AllowSlashChar: false, + AllowUnicodeCharsInUrls: true +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..2ce62b74a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettingsModel_ToDomain.verified.txt @@ -0,0 +1,21 @@ +{ + DisplayCartAfterAddingProduct: true, + DisplayWishlistAfterAddingProduct: false, + MaximumShoppingCartItems: 1000, + AllowOutOfStockItemsToBeAddedToWishlist: false, + MoveItemsFromWishlistToCart: false, + ShowProductImagesOnShoppingCart: false, + ShowProductImagesOnWishList: false, + ShowDiscountBox: true, + ShowGiftVoucherBox: false, + EmailWishlistEnabled: false, + AllowAnonymousUsersToEmailWishlist: false, + MiniShoppingCartEnabled: false, + ShowImagesInsidebarCart: false, + RoundPrices: false, + GroupTierPrices: false, + AllowCartItemEditing: false, + AllowOnHoldCart: false, + SharedCartBetweenStores: false, + AllowToSelectWarehouse: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettings_ToModel.verified.txt new file mode 100644 index 000000000..aa04e7d0f --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.ShoppingCartSettings_ToModel.verified.txt @@ -0,0 +1,23 @@ +{ + DisplayCartAfterAddingProduct: true, + DisplayWishlistAfterAddingProduct: false, + MaximumShoppingCartItems: 1000, + MaximumWishlistItems: 1000, + AllowOutOfStockItemsToBeAddedToWishlist: true, + MoveItemsFromWishlistToCart: true, + ShowProductImagesOnShoppingCart: true, + ShowProductImagesOnWishList: true, + ShowDiscountBox: true, + ShowGiftVoucherBox: true, + CrossSellsNumber: 4, + EmailWishlistEnabled: true, + AllowAnonymousUsersToEmailWishlist: false, + MiniShoppingCartEnabled: false, + ShowImagesInsidebarCart: false, + AllowCartItemEditing: false, + SharedCartBetweenStores: false, + AllowOnHoldCart: false, + AllowToSelectWarehouse: false, + GroupTierPrices: false, + RoundPrices: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..5fa3de0c1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettingsModel_ToDomain.verified.txt @@ -0,0 +1,21 @@ +{ + TaxDisplayType: 1, + DefaultTaxAddress: { + Id: ObjectId_1 + }, + DisplayTaxRates: false, + PricesIncludeTax: false, + AllowCustomersToSelectTaxDisplayType: false, + HideZeroTax: false, + HideTaxInOrderSummary: false, + ForceTaxExclusionFromOrderSubtotal: false, + ShippingIsTaxable: false, + ShippingPriceIncludesTax: false, + PaymentMethodAdditionalFeeIsTaxable: false, + PaymentMethodAdditionalFeeIncludesTax: false, + EuVatEnabled: false, + EuVatAllowVatExemption: false, + EuVatUseWebService: false, + EuVatAssumeValid: false, + CalculateRoundPrice: 2 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettings_ToModel.verified.txt new file mode 100644 index 000000000..f4cefef8c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.TaxSettings_ToModel.verified.txt @@ -0,0 +1,48 @@ +{ + PricesIncludeTax: false, + AllowCustomersToSelectTaxDisplayType: true, + TaxDisplayType: 10, + DisplayTaxRates: false, + HideZeroTax: false, + HideTaxInOrderSummary: false, + ForceTaxExclusionFromOrderSubtotal: false, + DefaultTaxAddress: { + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false + }, + ShippingIsTaxable: false, + ShippingPriceIncludesTax: false, + PaymentMethodAdditionalFeeIsTaxable: false, + PaymentMethodAdditionalFeeIncludesTax: false, + EuVatEnabled: false, + EuVatShopCountryId: , + EuVatAllowVatExemption: true, + EuVatUseWebService: false, + EuVatAssumeValid: false, + CalculateRoundPrice: 2 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs new file mode 100644 index 000000000..f9884f1a7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs @@ -0,0 +1,452 @@ +using AutoMapper; +using Grand.Domain.Admin; +using Grand.Domain.Blogs; +using Grand.Domain.Catalog; +using Grand.Domain.Common; +using Grand.Domain.Customers; +using Grand.Domain.Knowledgebase; +using Grand.Domain.Media; +using Grand.Domain.News; +using Grand.Domain.Orders; +using Grand.Domain.Payments; +using Grand.Domain.PushNotifications; +using Grand.Domain.Seo; +using Grand.Domain.Shipping; +using Grand.Domain.Tax; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Settings; +using Grand.Web.AdminShared.Models.Tax; +using Grand.Web.Common.Security.Captcha; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class SettingsMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── CatalogSettings ─────────────────────────────────────────────────────── + + [TestMethod] + public Task CatalogSettings_ToModel() + { + var source = new CatalogSettings { + AllowViewUnpublishedProductPage = false, + DisplayDiscontinuedMessageForUnpublishedProducts = true, + ShowSkuOnProductDetailsPage = true, + ShowSkuOnCatalogPages = false, + ShowGtin = false, + ShowFreeShippingNotification = true, + ShowProductImagesInSearchAutoComplete = false, + PageShareCode = "", + DefaultPageSizeOptions = "6, 3, 9", + SearchPageProductsPerPage = 6, + SearchPageAllowCustomersToSelectPageSize = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CatalogSettingsModel_ToDomain() + { + var model = new CatalogSettingsModel { + AllowViewUnpublishedProductPage = false, + ShowSkuOnProductDetailsPage = true, + SearchPageProductsPerPage = 6 + }; + return Verify(_mapper.Map(model)); + } + + // ── MediaSettings ───────────────────────────────────────────────────────── + + [TestMethod] + public Task MediaSettings_ToModel() + { + var source = new MediaSettings { + ProductThumbPictureSize = 415, + ProductDetailsPictureSize = 550, + ProductThumbPictureSizeOnProductDetailsPage = 100, + AssociatedProductPictureSize = 220, + CategoryThumbPictureSize = 450, + CollectionThumbPictureSize = 450, + BrandThumbPictureSize = 450, + VendorThumbPictureSize = 450, + CartThumbPictureSize = 80, + MaximumImageSize = 1980, + AutoCompleteSearchThumbPictureSize = 20 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MediaSettingsModel_ToDomain() + { + var model = new MediaSettingsModel { + ProductThumbPictureSize = 415, + MaximumImageSize = 1980 + }; + return Verify(_mapper.Map(model)); + } + + // ── CommonSettings ──────────────────────────────────────────────────────── + + [TestMethod] + public Task CommonSettings_ToModel() + { + var source = new CommonSettings { + StoreInDatabaseContactUsForm = true, + UseSystemEmailForContactUsForm = false, + SitemapEnabled = true, + SitemapIncludeCategories = true, + SitemapIncludeProducts = false, + PopupForTermsOfServiceLinks = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CommonSettingsModel_ToDomain() + { + var model = new GeneralCommonSettingsModel.CommonSettingsModel { + StoreInDatabaseContactUsForm = true, + SitemapEnabled = true + }; + return Verify(_mapper.Map(model)); + } + + // ── CaptchaSettings ─────────────────────────────────────────────────────── + + [TestMethod] + public Task CaptchaSettings_ToModel() + { + var source = new CaptchaSettings { + Enabled = true, + ShowOnLoginPage = true, + ShowOnRegistrationPage = false, + ShowOnContactUsPage = true, + ShowOnProductReviewPage = false, + ReCaptchaPublicKey = "public-key-123", + ReCaptchaPrivateKey = "private-key-456", + ReCaptchaVersion = GoogleReCaptchaVersion.V2 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task SecuritySettingsModel_ToDomain() + { + var model = new GeneralCommonSettingsModel.SecuritySettingsModel { + CaptchaEnabled = true, + CaptchaShowOnLoginPage = true, + ReCaptchaPublicKey = "public-key-123", + ReCaptchaPrivateKey = "private-key-456" + }; + return Verify(_mapper.Map(model)); + } + + // ── SeoSettings ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task SeoSettings_ToModel() + { + var source = new SeoSettings { + PageTitleSeparator = " | ", + PageTitleSeoAdjustment = true, + DefaultTitle = "Grand Store", + DefaultMetaKeywords = "store, online", + DefaultMetaDescription = "Best online store", + GenerateProductMetaDescription = true, + ConvertNonWesternChars = false, + AllowUnicodeCharsInUrls = true, + CanonicalUrlsEnabled = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task SeoSettingsModel_ToDomain() + { + var model = new GeneralCommonSettingsModel.SeoSettingsModel { + PageTitleSeparator = " | ", + DefaultTitle = "Grand Store", + AllowUnicodeCharsInUrls = true + }; + return Verify(_mapper.Map(model)); + } + + // ── AdminSearchSettings ─────────────────────────────────────────────────── + + [TestMethod] + public Task AdminSearchSettings_ToModel() + { + var source = new AdminSearchSettings { + SearchInProducts = true, + SearchInCategories = true, + SearchInBlogs = true, + SearchInOrders = true, + SearchInCustomers = true, + SearchInNews = false, + SearchInPages = true, + MinSearchTermLength = 3, + MaxSearchResultsCount = 10 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task AdminSearchSettingsModel_ToDomain() + { + var model = new AdminSearchSettingsModel { + SearchInProducts = true, + SearchInCategories = true, + MinSearchTermLength = 3, + MaxSearchResultsCount = 10 + }; + return Verify(_mapper.Map(model)); + } + + // ── ShoppingCartSettings ────────────────────────────────────────────────── + + [TestMethod] + public Task ShoppingCartSettings_ToModel() + { + var source = new ShoppingCartSettings { + DisplayCartAfterAddingProduct = true, + DisplayWishlistAfterAddingProduct = false, + MaximumShoppingCartItems = 1000, + MaximumWishlistItems = 1000, + AllowOutOfStockItemsToBeAddedToWishlist = true, + MoveItemsFromWishlistToCart = true, + ShowProductImagesOnShoppingCart = true, + ShowProductImagesOnWishList = true, + ShowDiscountBox = true, + ShowGiftVoucherBox = true, + CrossSellsNumber = 4, + EmailWishlistEnabled = true, + AllowAnonymousUsersToEmailWishlist = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ShoppingCartSettingsModel_ToDomain() + { + var model = new SalesSettingsModel.ShoppingCartSettingsModel { + DisplayCartAfterAddingProduct = true, + MaximumShoppingCartItems = 1000, + ShowDiscountBox = true + }; + return Verify(_mapper.Map(model)); + } + + // ── LoyaltyPointsSettings ───────────────────────────────────────────────── + + [TestMethod] + public Task LoyaltyPointsSettings_ToModel() + { + var source = new LoyaltyPointsSettings { + Enabled = true, + ExchangeRate = 0.01, + MinimumLoyaltyPointsToUse = 0, + PointsForRegistration = 100, + PointsForPurchases_Amount = 10, + PointsForPurchases_Points = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task LoyaltyPointsSettingsModel_ToDomain() + { + var model = new SalesSettingsModel.LoyaltyPointsSettingsModel { + Enabled = true, + ExchangeRate = 0.01, + PointsForRegistration = 100 + }; + return Verify(_mapper.Map(model)); + } + + // ── OrderSettings ───────────────────────────────────────────────────────── + + [TestMethod] + public Task OrderSettings_ToModel() + { + var source = new OrderSettings { + IsReOrderAllowed = true, + MinOrderSubtotalAmount = 0, + MinOrderSubtotalAmountIncludingTax = false, + MinOrderTotalAmount = 0, + AnonymousCheckoutAllowed = true, + TermsOfServiceOnShoppingCartPage = true, + TermsOfServiceOnOrderConfirmPage = false, + GiftVouchers_Activated_OrderStatusId = 30 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task OrderSettingsModel_ToDomain() + { + var model = new SalesSettingsModel.OrderSettingsModel { + IsReOrderAllowed = true, + AnonymousCheckoutAllowed = true + }; + return Verify(_mapper.Map(model)); + } + + // ── TaxSettings ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task TaxSettings_ToModel() + { + var source = new TaxSettings { + PricesIncludeTax = false, + AllowCustomersToSelectTaxDisplayType = true, + TaxDisplayType = TaxDisplayType.ExcludingTax, + DisplayTaxRates = false, + HideZeroTax = false, + HideTaxInOrderSummary = false, + ForceTaxExclusionFromOrderSubtotal = false, + ShippingIsTaxable = false, + EuVatEnabled = false, + EuVatShopCountryId = "", + EuVatAllowVatExemption = true, + EuVatUseWebService = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task TaxSettingsModel_ToDomain() + { + var model = new TaxSettingsModel { + PricesIncludeTax = false, + TaxDisplayType = 1, + EuVatEnabled = false + }; + return Verify(_mapper.Map(model)); + } + + // ── PushNotificationsSettings ───────────────────────────────────────────── + + [TestMethod] + public Task PushNotificationsSettings_ToModel() + { + var source = new PushNotificationsSettings { + Enabled = false, + AllowGuestNotifications = true, + AuthDomain = "", + DatabaseUrl = "", + ProjectId = "", + PublicApiKey = "", + SenderId = "", + StorageBucket = "", + AppId = "" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task PushNotificationsSettingsModel_ToDomain() + { + var model = new PushNotificationsSettingsModel { + Enabled = false, + AllowGuestNotifications = true + }; + return Verify(_mapper.Map(model)); + } + + // ── CustomerSettings ────────────────────────────────────────────────────── + + [TestMethod] + public Task CustomerSettings_ToModel() + { + var source = new CustomerSettings { + UsernamesEnabled = false, + CheckUsernameAvailabilityEnabled = false, + AllowUsersToChangeUsernames = true, + NotifyNewCustomerRegistration = true, + NewsletterEnabled = true, + GeoEnabled = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CustomerSettingsModel_ToDomain() + { + var model = new CustomerSettingsModel.CustomersSettingsModel { + UsernamesEnabled = false, + DefaultPasswordFormat = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── AddressSettings ─────────────────────────────────────────────────────── + + [TestMethod] + public Task AddressSettings_ToModel() + { + var source = new AddressSettings { + NameEnabled = true, + CompanyEnabled = true, + CompanyRequired = false, + CountryEnabled = true, + StateProvinceEnabled = true, + CityEnabled = true, + CityRequired = false, + StreetAddressEnabled = true, + StreetAddressRequired = true, + StreetAddress2Enabled = false, + ZipPostalCodeEnabled = true, + ZipPostalCodeRequired = true, + PhoneEnabled = true, + PhoneRequired = false, + FaxEnabled = false, + FaxRequired = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task AddressSettingsModel_ToDomain() + { + var model = new CustomerSettingsModel.AddressSettingsModel { + NameEnabled = true, + CompanyEnabled = true, + CountryEnabled = true, + StreetAddressEnabled = true + }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeModel_ToDomain.verified.txt new file mode 100644 index 000000000..3bf707f8e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeModel_ToDomain.verified.txt @@ -0,0 +1,18 @@ +{ + Name: Gift Wrapping, + TextPrompt: Add gift wrapping?, + IsRequired: false, + ShippableProductRequired: false, + IsTaxExempt: false, + AttributeControlTypeId: TextBox, + DisplayOrder: 1, + LimitedToGroups: true, + CustomerGroups: [ + grp-001 + ], + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValueModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValueModel_ToDomain.verified.txt new file mode 100644 index 000000000..b24a96f69 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValueModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Yes, + PriceAdjustment: 3.99, + IsPreSelected: false, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValue_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValue_ToModel.verified.txt new file mode 100644 index 000000000..fcdec9aac --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttributeValue_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + Name: Yes, + DisplayColorSquaresRgb: false, + PriceAdjustment: 3.99, + IsPreSelected: false, + DisplayOrder: 1, + Id: cav-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttribute_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttribute_ToModel.verified.txt new file mode 100644 index 000000000..2d56afac4 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.CheckoutAttribute_ToModel.verified.txt @@ -0,0 +1,12 @@ +{ + Name: Gift Wrapping, + TextPrompt: Add gift wrapping?, + IsRequired: false, + ShippableProductRequired: false, + IsTaxExempt: false, + TaxCategoryId: tc-001, + AttributeControlTypeId: 4, + DisplayOrder: 1, + ConditionAllowed: false, + Id: ca-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..1edc602cd --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettingsModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + AllowRePostingPayments: true, + SkipPaymentIfOnlyOne: false, + ShowPaymentIfCartIsZero: false, + ShowPaymentDescriptions: true, + SkipPaymentInfo: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettings_ToModel.verified.txt new file mode 100644 index 000000000..7cb8b3728 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.PaymentSettings_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + AllowRePostingPayments: true, + SkipPaymentIfOnlyOne: true, + ShowPaymentIfCartIsZero: false, + ShowPaymentDescriptions: true, + SkipPaymentInfo: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethodModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethodModel_ToDomain.verified.txt new file mode 100644 index 000000000..6da84fae0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethodModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Standard Shipping, + Description: 5-7 business days, + DisplayOrder: 1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethod_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethod_ToModel.verified.txt new file mode 100644 index 000000000..5a1a430ad --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingMethod_ToModel.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Standard Shipping, + Description: 5-7 business days, + DisplayOrder: 1, + Id: sm-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..ac77c994c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettingsModel_ToDomain.verified.txt @@ -0,0 +1,11 @@ +{ + AllowPickUpInStore: true, + FreeShippingOverXEnabled: true, + FreeShippingOverXValue: 100.0, + FreeShippingOverXIncludingTax: false, + EstimateShippingEnabled: true, + DisplayShipmentEventsToCustomers: false, + DisplayShipmentEventsToStoreOwner: false, + SkipShippingMethodSelectionIfOnlyOne: false, + AdditionalShippingChargeByQty: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettings_ToModel.verified.txt new file mode 100644 index 000000000..90ac250d9 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.ShippingSettings_ToModel.verified.txt @@ -0,0 +1,11 @@ +{ + AllowPickUpInStore: true, + FreeShippingOverXEnabled: true, + FreeShippingOverXValue: 100.0, + FreeShippingOverXIncludingTax: false, + EstimateShippingEnabled: true, + DisplayShipmentEventsToCustomers: true, + DisplayShipmentEventsToStoreOwner: false, + SkipShippingMethodSelectionIfOnlyOne: true, + AdditionalShippingChargeByQty: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs new file mode 100644 index 000000000..fb72d8e07 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs @@ -0,0 +1,170 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Domain.Orders; +using Grand.Domain.Payments; +using Grand.Domain.Shipping; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Orders; +using Grand.Web.AdminShared.Models.Payments; +using Grand.Web.AdminShared.Models.Shipping; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class ShippingMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── ShippingMethod ──────────────────────────────────────────────────────── + + [TestMethod] + public Task ShippingMethod_ToModel() + { + var source = new ShippingMethod { + Id = "sm-001", + Name = "Standard Shipping", + Description = "5-7 business days", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ShippingMethodModel_ToDomain() + { + var model = new ShippingMethodModel { + Name = "Standard Shipping", + Description = "5-7 business days", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── ShippingSettings ────────────────────────────────────────────────────── + + [TestMethod] + public Task ShippingSettings_ToModel() + { + var source = new ShippingSettings { + FreeShippingOverXEnabled = true, + FreeShippingOverXValue = 100, + FreeShippingOverXIncludingTax = false, + EstimateShippingEnabled = true, + DisplayShipmentEventsToCustomers = true, + DisplayShipmentEventsToStoreOwner = false, + SkipShippingMethodSelectionIfOnlyOne = true, + AllowPickUpInStore = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ShippingSettingsModel_ToDomain() + { + var model = new ShippingSettingsModel { + FreeShippingOverXEnabled = true, + FreeShippingOverXValue = 100, + EstimateShippingEnabled = true, + AllowPickUpInStore = true + }; + return Verify(_mapper.Map(model)); + } + + // ── PaymentSettings ─────────────────────────────────────────────────────── + + [TestMethod] + public Task PaymentSettings_ToModel() + { + var source = new PaymentSettings { + AllowRePostingPayments = true, + SkipPaymentIfOnlyOne = true, + ShowPaymentDescriptions = true, + SkipPaymentInfo = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task PaymentSettingsModel_ToDomain() + { + var model = new PaymentSettingsModel { + AllowRePostingPayments = true, + ShowPaymentDescriptions = true + }; + return Verify(_mapper.Map(model)); + } + + // ── CheckoutAttribute ───────────────────────────────────────────────────── + + [TestMethod] + public Task CheckoutAttribute_ToModel() + { + var source = new CheckoutAttribute { + Id = "ca-001", + Name = "Gift Wrapping", + TextPrompt = "Add gift wrapping?", + IsRequired = false, + AttributeControlTypeId = (AttributeControlType)4, + DisplayOrder = 1, + TaxCategoryId = "tc-001", + ShippableProductRequired = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CheckoutAttributeModel_ToDomain() + { + var model = new CheckoutAttributeModel { + Name = "Gift Wrapping", + TextPrompt = "Add gift wrapping?", + IsRequired = false, + AttributeControlTypeId = 4, + DisplayOrder = 1, + CustomerGroups = ["grp-001"], + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + [TestMethod] + public Task CheckoutAttributeValue_ToModel() + { + var source = new CheckoutAttributeValue { + Id = "cav-001", + Name = "Yes", + PriceAdjustment = 3.99, + WeightAdjustment = 0, + IsPreSelected = false, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CheckoutAttributeValueModel_ToDomain() + { + var model = new CheckoutAttributeValueModel { + Name = "Yes", + PriceAdjustment = 3.99, + IsPreSelected = false, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.AdminSiteMap_ToMenuModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.AdminSiteMap_ToMenuModel.verified.txt new file mode 100644 index 000000000..46d39b443 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.AdminSiteMap_ToMenuModel.verified.txt @@ -0,0 +1,12 @@ +{ + SystemName: Dashboard, + ResourceName: Admin.Dashboard, + ControllerName: Admin, + ActionName: Dashboard, + Url: /Admin/Dashboard, + IconClass: fa fa-home, + DisplayOrder: 1, + OpenUrlInNewTab: false, + AllPermissions: false, + Id: menu-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.CampaignModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.CampaignModel_ToDomain.verified.txt new file mode 100644 index 000000000..efb241267 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.CampaignModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Summer Campaign, + Subject: Summer Sale!, + Body: Check our summer deals, + StoreId: store-001, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Campaign_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Campaign_ToModel.verified.txt new file mode 100644 index 000000000..6d49fbe67 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Campaign_ToModel.verified.txt @@ -0,0 +1,7 @@ +{ + Name: Summer Campaign, + Subject: Summer Sale!, + Body: Check our summer deals, + StoreId: store-001, + Id: camp-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccountModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccountModel_ToDomain.verified.txt new file mode 100644 index 000000000..58c8de5c0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccountModel_ToDomain.verified.txt @@ -0,0 +1,9 @@ +{ + Email: noreply@store.com, + DisplayName: Grand Store, + Host: smtp.example.com, + Port: 587, + Username: noreply@store.com, + UseServerCertificateValidation: false, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccount_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccount_ToModel.verified.txt new file mode 100644 index 000000000..afd726320 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.EmailAccount_ToModel.verified.txt @@ -0,0 +1,10 @@ +{ + Email: noreply@store.com, + DisplayName: Grand Store, + Host: smtp.example.com, + Port: 587, + Username: noreply@store.com, + UseServerCertificateValidation: false, + IsDefaultEmailAccount: false, + Id: ea-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MenuModel_ToAdminSiteMap.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MenuModel_ToAdminSiteMap.verified.txt new file mode 100644 index 000000000..63b6f527b --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MenuModel_ToAdminSiteMap.verified.txt @@ -0,0 +1,11 @@ +{ + SystemName: Dashboard, + ResourceName: Admin.Dashboard, + ControllerName: Admin, + ActionName: Dashboard, + IconClass: fa fa-home, + DisplayOrder: 1, + OpenUrlInNewTab: false, + AllPermissions: false, + Id: menu-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplateModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplateModel_ToDomain.verified.txt new file mode 100644 index 000000000..9693bb272 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplateModel_ToDomain.verified.txt @@ -0,0 +1,12 @@ +{ + Name: OrderPlaced.CustomerNotification, + Subject: Your order has been placed, + Body: Order body, + IsActive: true, + EmailAccountId: ea-001, + LimitedToStores: true, + Stores: [ + store-001 + ], + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplate_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplate_ToModel.verified.txt new file mode 100644 index 000000000..6d883adf6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.MessageTemplate_ToModel.verified.txt @@ -0,0 +1,15 @@ +{ + Name: OrderPlaced.CustomerNotification, + BccEmailAddresses: , + Subject: Your order has been placed, + Body: Order body, + IsActive: true, + SendImmediately: false, + DelayBeforeSend: 0, + HasAttachedDownload: false, + EmailAccountId: ea-001, + Stores: [ + store-001 + ], + Id: mt-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmailModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmailModel_ToDomain.verified.txt new file mode 100644 index 000000000..4645757c0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmailModel_ToDomain.verified.txt @@ -0,0 +1,7 @@ +{ + From: from@store.com, + To: customer@example.com, + Subject: Order Confirmation, + Body:

Thank you!

, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmail_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmail_ToModel.verified.txt new file mode 100644 index 000000000..443c72509 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.QueuedEmail_ToModel.verified.txt @@ -0,0 +1,13 @@ +{ + Id: qe-001, + From: from@store.com, + FromName: Store, + To: customer@example.com, + ToName: Customer, + ReplyTo: , + CC: , + Bcc: , + Subject: Order Confirmation, + Body:

Thank you!

, + SendImmediately: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTaskModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTaskModel_ToDomain.verified.txt new file mode 100644 index 000000000..8fcce0481 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTaskModel_ToDomain.verified.txt @@ -0,0 +1,6 @@ +{ + Enabled: true, + StopOnError: false, + TimeInterval: 60, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTask_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTask_ToModel.verified.txt new file mode 100644 index 000000000..7aa6c133d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.ScheduleTask_ToModel.verified.txt @@ -0,0 +1,8 @@ +{ + ScheduleTaskName: Grand.Services.Catalog.UpdateExchangeRateTask, Grand.Services, + Enabled: true, + StopOnError: false, + TimeInterval: 60, + StoreId: , + Id: st-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorModel_ToDomain.verified.txt new file mode 100644 index 000000000..f95607219 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorModel_ToDomain.verified.txt @@ -0,0 +1,14 @@ +{ + Name: Best Vendor, + Email: vendor@example.com, + Description: Top vendor, + Active: false, + Deleted: false, + PageSize: 5, + AllowCustomersToSelectPageSize: false, + AllowCustomerReviews: false, + Address: { + Id: ObjectId_1 + }, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain.verified.txt new file mode 100644 index 000000000..8e878d4c6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain.verified.txt @@ -0,0 +1,57 @@ +{ + VendorsBlockItemsToDisplay: 3, + ShowVendorOnProductDetailsPage: true, + AllowCustomersToContactVendors: true, + AllowCustomersToApplyForVendorAccount: false, + AllowSearchByVendor: false, + TermsOfServiceEnabled: false, + AllowVendorsToEditInfo: false, + NotifyStoreOwnerAboutVendorInformationChange: false, + VendorReviewsMustBeApproved: false, + AllowAnonymousUsersToReviewVendor: false, + VendorReviewPossibleOnlyAfterPurchasing: false, + NotifyVendorAboutNewVendorReviews: false, + DefaultAllowCustomerReview: false, + VendorReviewPossibleOnlyOnce: false, + CompanyEnabled: false, + CompanyRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + CityEnabled: false, + CityRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + NoteEnabled: false, + FaxRequired: false, + AddressSettings: { + NameEnabled: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + CityEnabled: false, + CityRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + DisallowUsersToChangeEmail: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel.verified.txt new file mode 100644 index 000000000..87034aff1 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel.verified.txt @@ -0,0 +1,35 @@ +{ + VendorsBlockItemsToDisplay: 3, + ShowVendorOnProductDetailsPage: true, + AllowCustomersToContactVendors: true, + AllowCustomersToApplyForVendorAccount: false, + AllowSearchByVendor: true, + AllowVendorsToEditInfo: false, + NotifyStoreOwnerAboutVendorInformationChange: true, + TermsOfServiceEnabled: false, + VendorReviewsMustBeApproved: false, + AllowAnonymousUsersToReviewVendor: false, + VendorReviewPossibleOnlyAfterPurchasing: false, + NotifyVendorAboutNewVendorReviews: false, + DefaultAllowCustomerReview: false, + NumberOfReview: 10, + VendorReviewPossibleOnlyOnce: false, + AddressSettings: { + CompanyEnabled: false, + CompanyRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + CityEnabled: false, + CityRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Vendor_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Vendor_ToModel.verified.txt new file mode 100644 index 000000000..b0644d798 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.Vendor_ToModel.verified.txt @@ -0,0 +1,45 @@ +{ + Name: Best Vendor, + Email: vendor@example.com, + Description: Top vendor, + PictureId: pic-vendor, + AdminComment: Internal note, + Active: true, + DisplayOrder: 1, + AllowCustomerReviews: true, + SeName: best-vendor, + PageSize: 10, + AllowCustomersToSelectPageSize: false, + PageSizeOptions: 10, 20, + Address: { + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + Id: ObjectId_1 + }, + Id: vendor-001 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs new file mode 100644 index 000000000..5172e53ae --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs @@ -0,0 +1,287 @@ +using AutoMapper; +using Grand.Domain.Admin; +using Grand.Domain.Customers; +using Grand.Domain.Messages; +using Grand.Domain.Tasks; +using Grand.Web.AdminShared.Mapper; +using Grand.Web.AdminShared.Models.Customers; +using Grand.Web.AdminShared.Models.Menu; +using Grand.Web.AdminShared.Models.Messages; +using Grand.Web.AdminShared.Models.Settings; +using Grand.Web.AdminShared.Models.Tasks; +using Grand.Web.AdminShared.Models.Vendors; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using DomainVendor = Grand.Domain.Vendors.Vendor; +using VendorSettings = Grand.Domain.Vendors.VendorSettings; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.AdminShared; + +[TestClass] +public class SystemMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── EmailAccount ────────────────────────────────────────────────────────── + + [TestMethod] + public Task EmailAccount_ToModel() + { + var source = new EmailAccount { + Id = "ea-001", + Email = "noreply@store.com", + DisplayName = "Grand Store", + Host = "smtp.example.com", + Port = 587, + Username = "noreply@store.com" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task EmailAccountModel_ToDomain() + { + var model = new EmailAccountModel { + Email = "noreply@store.com", + DisplayName = "Grand Store", + Host = "smtp.example.com", + Port = 587, + Username = "noreply@store.com" + }; + return Verify(_mapper.Map(model)); + } + + // ── QueuedEmail ─────────────────────────────────────────────────────────── + + [TestMethod] + public Task QueuedEmail_ToModel() + { + var source = new QueuedEmail { + Id = "qe-001", + PriorityId = QueuedEmailPriority.High, + From = "from@store.com", + FromName = "Store", + To = "customer@example.com", + ToName = "Customer", + ReplyTo = "", + CC = "", + Bcc = "", + Subject = "Order Confirmation", + Body = "

Thank you!

", + SentTries = 0 + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task QueuedEmailModel_ToDomain() + { + var model = new QueuedEmailModel { + From = "from@store.com", + To = "customer@example.com", + Subject = "Order Confirmation", + Body = "

Thank you!

" + }; + return Verify(_mapper.Map(model)); + } + + // ── MessageTemplate ─────────────────────────────────────────────────────── + + [TestMethod] + public Task MessageTemplate_ToModel() + { + var source = new MessageTemplate { + Id = "mt-001", + Name = "OrderPlaced.CustomerNotification", + BccEmailAddresses = "", + Subject = "Your order has been placed", + Body = "Order body", + IsActive = true, + EmailAccountId = "ea-001", + DelayBeforeSend = 0, + Stores = ["store-001"] + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MessageTemplateModel_ToDomain() + { + var model = new MessageTemplateModel { + Name = "OrderPlaced.CustomerNotification", + Subject = "Your order has been placed", + Body = "Order body", + IsActive = true, + EmailAccountId = "ea-001", + Stores = ["store-001"] + }; + return Verify(_mapper.Map(model)); + } + + // ── Campaign ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Campaign_ToModel() + { + var source = new Campaign { + Id = "camp-001", + Name = "Summer Campaign", + Subject = "Summer Sale!", + Body = "Check our summer deals", + StoreId = "store-001" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task CampaignModel_ToDomain() + { + var model = new CampaignModel { + Name = "Summer Campaign", + Subject = "Summer Sale!", + Body = "Check our summer deals", + StoreId = "store-001" + }; + return Verify(_mapper.Map(model)); + } + + // ── AdminSiteMap (Menu) ─────────────────────────────────────────────────── + + [TestMethod] + public Task AdminSiteMap_ToMenuModel() + { + var source = new AdminSiteMap { + Id = "menu-001", + SystemName = "Dashboard", + ResourceName = "Admin.Dashboard", + ControllerName = "Admin", + ActionName = "Dashboard", + IconClass = "fa fa-home", + DisplayOrder = 1, + Url = "/Admin/Dashboard" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task MenuModel_ToAdminSiteMap() + { + var model = new MenuModel { + Id = "menu-001", + SystemName = "Dashboard", + ResourceName = "Admin.Dashboard", + ControllerName = "Admin", + ActionName = "Dashboard", + IconClass = "fa fa-home", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(model)); + } + + // ── ScheduleTask ────────────────────────────────────────────────────────── + + [TestMethod] + public Task ScheduleTask_ToModel() + { + var source = new ScheduleTask { + Id = "st-001", + ScheduleTaskName = "Grand.Services.Catalog.UpdateExchangeRateTask, Grand.Services", + Enabled = true, + StopOnError = false, + TimeInterval = 60, + StoreId = "" + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ScheduleTaskModel_ToDomain() + { + var model = new ScheduleTaskModel { + TimeInterval = 60, + Enabled = true, + StopOnError = false + }; + return Verify(_mapper.Map(model)); + } + + // ── Vendor ──────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Vendor_ToModel() + { + var source = new DomainVendor { + Id = "vendor-001", + Name = "Best Vendor", + Email = "vendor@example.com", + Description = "Top vendor", + AdminComment = "Internal note", + PictureId = "pic-vendor", + PageSize = 10, + AllowCustomersToSelectPageSize = false, + PageSizeOptions = "10, 20", + Active = true, + DisplayOrder = 1, + SeName = "best-vendor", + AllowCustomerReviews = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task VendorModel_ToDomain() + { + var model = new VendorModel { + Name = "Best Vendor", + Email = "vendor@example.com", + Description = "Top vendor", + Active = true + }; + return Verify(_mapper.Map(model)); + } + + // ── VendorSettings ──────────────────────────────────────────────────────── + + [TestMethod] + public Task VendorSettings_ToModel() + { + var source = new VendorSettings { + VendorsBlockItemsToDisplay = 3, + ShowVendorOnProductDetailsPage = true, + AllowCustomersToContactVendors = true, + AllowCustomersToApplyForVendorAccount = false, + AllowSearchByVendor = true, + AllowVendorsToEditInfo = false, + NotifyStoreOwnerAboutVendorInformationChange = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task VendorSettingsModel_ToDomain() + { + var model = new VendorSettingsModel { + VendorsBlockItemsToDisplay = 3, + ShowVendorOnProductDetailsPage = true, + AllowCustomersToContactVendors = true + }; + return Verify(_mapper.Map(model)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.AddressDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.AddressDto_ToEntity.verified.txt new file mode 100644 index 000000000..3f00632e0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.AddressDto_ToEntity.verified.txt @@ -0,0 +1,11 @@ +{ + FirstName: John, + LastName: Doe, + Email: john@example.com, + CountryId: US, + City: Springfield, + Address1: 123 Main St, + ZipPostalCode: 12345, + PhoneNumber: 123-456-7890, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Address_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Address_ToDto.verified.txt new file mode 100644 index 000000000..7e7ef8beb --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Address_ToDto.verified.txt @@ -0,0 +1,11 @@ +{ + Id: addr-1, + FirstName: John, + LastName: Doe, + Email: john@example.com, + CountryId: US, + City: Springfield, + Address1: 123 Main St, + ZipPostalCode: 12345, + PhoneNumber: 123-456-7890 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.BrandDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.BrandDto_ToEntity.verified.txt new file mode 100644 index 000000000..3175dfef7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.BrandDto_ToEntity.verified.txt @@ -0,0 +1,17 @@ +{ + Name: Nike, + Description: Sports brand, + MetaTitle: Nike, + PageSize: 12, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + ShowOnHomePage: true, + IncludeInMenu: false, + DefaultSort: -1, + Published: true, + DisplayOrder: 1, + LimitedToGroups: false, + SeName: nike, + LimitedToStores: false, + Id: brand-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Brand_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Brand_ToDto.verified.txt new file mode 100644 index 000000000..0a043d19e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Brand_ToDto.verified.txt @@ -0,0 +1,14 @@ +{ + Name: Nike, + SeName: nike, + Description: Sports brand, + MetaTitle: Nike, + PageSize: 12, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + ShowOnHomePage: true, + IncludeInMenu: false, + Published: true, + DisplayOrder: 1, + Id: brand-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CategoryDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CategoryDto_ToEntity.verified.txt new file mode 100644 index 000000000..84464dce2 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CategoryDto_ToEntity.verified.txt @@ -0,0 +1,18 @@ +{ + Name: Electronics, + Description: Electronic items, + MetaTitle: Electronics, + PageSize: 9, + AllowCustomersToSelectPageSize: true, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + ShowOnSearchBox: false, + IncludeInMenu: false, + Published: true, + DefaultSort: -1, + HideOnCatalog: false, + LimitedToGroups: false, + SeName: electronics, + LimitedToStores: false, + Id: cat-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Category_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Category_ToDto.verified.txt new file mode 100644 index 000000000..c45f280ee --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Category_ToDto.verified.txt @@ -0,0 +1,15 @@ +{ + Name: Electronics, + Description: Electronic items, + MetaTitle: Electronics, + SeName: electronics, + PageSize: 9, + AllowCustomersToSelectPageSize: true, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + IncludeInMenu: false, + Published: true, + HideOnCatalog: false, + ShowOnSearchBox: false, + Id: cat-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CollectionDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CollectionDto_ToEntity.verified.txt new file mode 100644 index 000000000..d25743938 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CollectionDto_ToEntity.verified.txt @@ -0,0 +1,16 @@ +{ + Name: Summer 2025, + Description: Summer collection, + PageSize: 12, + AllowCustomersToSelectPageSize: false, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + IncludeInMenu: false, + DefaultSort: -1, + Published: true, + DisplayOrder: 1, + LimitedToGroups: false, + SeName: summer-2025, + LimitedToStores: false, + Id: col-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Collection_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Collection_ToDto.verified.txt new file mode 100644 index 000000000..40b7267f6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Collection_ToDto.verified.txt @@ -0,0 +1,13 @@ +{ + Name: Summer 2025, + SeName: summer-2025, + Description: Summer collection, + PageSize: 12, + AllowCustomersToSelectPageSize: false, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + IncludeInMenu: false, + Published: true, + DisplayOrder: 1, + Id: col-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerDto_ToEntity.verified.txt new file mode 100644 index 000000000..67ca818b7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerDto_ToEntity.verified.txt @@ -0,0 +1,12 @@ +{ + CustomerGuid: Guid_1, + Username: customer1, + Email: customer@example.com, + IsTaxExempt: false, + FreeShipping: false, + Active: true, + Deleted: false, + IsSystemAccount: false, + HasContributions: false, + Id: cust-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroupDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroupDto_ToEntity.verified.txt new file mode 100644 index 000000000..a8b0eb996 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroupDto_ToEntity.verified.txt @@ -0,0 +1,10 @@ +{ + Name: VIP Customers, + FreeShipping: true, + TaxExempt: false, + Active: false, + IsSystem: false, + SystemName: Vip, + EnablePasswordLifetime: false, + Id: cg-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroup_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroup_ToDto.verified.txt new file mode 100644 index 000000000..a8b0eb996 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.CustomerGroup_ToDto.verified.txt @@ -0,0 +1,10 @@ +{ + Name: VIP Customers, + FreeShipping: true, + TaxExempt: false, + Active: false, + IsSystem: false, + SystemName: Vip, + EnablePasswordLifetime: false, + Id: cg-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_EmptyUserFields.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_EmptyUserFields.verified.txt new file mode 100644 index 000000000..3e3ca300f --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_EmptyUserFields.verified.txt @@ -0,0 +1,10 @@ +{ + CustomerGuid: Guid_1, + Username: customer1, + Email: customer@example.com, + IsTaxExempt: false, + FreeShipping: false, + Active: true, + Deleted: false, + Id: cust-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PictureDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PictureDto_ToEntity.verified.txt new file mode 100644 index 000000000..3f73ef77c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PictureDto_ToEntity.verified.txt @@ -0,0 +1,8 @@ +{ + MimeType: image/jpeg, + SeoFilename: product-image, + AltAttribute: Product image, + TitleAttribute: Product, + IsNew: false, + Id: pic-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Picture_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Picture_ToDto.verified.txt new file mode 100644 index 000000000..3f73ef77c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Picture_ToDto.verified.txt @@ -0,0 +1,8 @@ +{ + MimeType: image/jpeg, + SeoFilename: product-image, + AltAttribute: Product image, + TitleAttribute: Product, + IsNew: false, + Id: pic-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PredefinedProductAttributeValue_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PredefinedProductAttributeValue_ToDto.verified.txt new file mode 100644 index 000000000..f4ef4f472 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.PredefinedProductAttributeValue_ToDto.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Red, + IsPreSelected: false, + DisplayOrder: 1, + Id: ppav-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeDto_ToEntity.verified.txt new file mode 100644 index 000000000..1636ef37a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeDto_ToEntity.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Color, + Description: Product color, + LimitedToStores: false, + Id: pa-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMappingDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMappingDto_ToEntity.verified.txt new file mode 100644 index 000000000..16b553e66 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMappingDto_ToEntity.verified.txt @@ -0,0 +1,9 @@ +{ + ProductAttributeId: pa-1, + TextPrompt: Choose color, + IsRequired: true, + ShowOnCatalogPage: false, + Combination: false, + AttributeControlTypeId: DropdownList, + Id: pam-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMapping_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMapping_ToDto.verified.txt new file mode 100644 index 000000000..731a2d508 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttributeMapping_ToDto.verified.txt @@ -0,0 +1,7 @@ +{ + ProductAttributeId: pa-1, + TextPrompt: Choose color, + IsRequired: true, + AttributeControlTypeId: DropdownList, + Id: pam-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttribute_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttribute_ToDto.verified.txt new file mode 100644 index 000000000..d4d20f225 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductAttribute_ToDto.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Color, + Description: Product color, + Id: pa-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductDto_ToEntity.verified.txt new file mode 100644 index 000000000..362d74650 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductDto_ToEntity.verified.txt @@ -0,0 +1,44 @@ +{ + VisibleIndividually: false, + Name: Test Product, + ShortDescription: Short, + FullDescription: Full description, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + Sku: SKU001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: false, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockQuantity: 100, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 29.99, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + AuctionEnded: false, + Published: true, + LimitedToGroups: false, + LimitedToStores: false, + Id: prod-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductTierPriceDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductTierPriceDto_ToEntity.verified.txt new file mode 100644 index 000000000..11ec65bc6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.ProductTierPriceDto_ToEntity.verified.txt @@ -0,0 +1,7 @@ +{ + StoreId: , + CustomerGroupId: , + Quantity: 10, + Price: 9.99, + Id: tp-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Product_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Product_ToDto.verified.txt new file mode 100644 index 000000000..4d431c979 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Product_ToDto.verified.txt @@ -0,0 +1,41 @@ +{ + VisibleIndividually: false, + Name: Test Product, + ShortDescription: Short, + FullDescription: Full description, + ShowOnHomePage: false, + AllowCustomerReviews: false, + Sku: SKU001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: false, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockQuantity: 100, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 29.99, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + AuctionEnded: false, + Published: true, + Id: prod-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeDto_ToEntity.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeDto_ToEntity.verified.txt new file mode 100644 index 000000000..244f2ef9e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeDto_ToEntity.verified.txt @@ -0,0 +1,6 @@ +{ + Name: Material, + DisplayOrder: 1, + LimitedToStores: false, + Id: sa-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeOption_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeOption_ToDto.verified.txt new file mode 100644 index 000000000..ecd59d72c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttributeOption_ToDto.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Cotton, + ColorSquaresRgb: #FFFFFF, + Id: sao-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttribute_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttribute_ToDto.verified.txt new file mode 100644 index 000000000..e83755ebf --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.SpecificationAttribute_ToDto.verified.txt @@ -0,0 +1,5 @@ +{ + Name: Material, + DisplayOrder: 1, + Id: sa-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.TierPrice_ToDto.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.TierPrice_ToDto.verified.txt new file mode 100644 index 000000000..11ec65bc6 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.TierPrice_ToDto.verified.txt @@ -0,0 +1,7 @@ +{ + StoreId: , + CustomerGroupId: , + Quantity: 10, + Price: 9.99, + Id: tp-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs new file mode 100644 index 000000000..df759febc --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs @@ -0,0 +1,438 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Domain.Common; +using Grand.Domain.Customers; +using Grand.Domain.Media; +using Grand.Module.Api.DTOs.Catalog; +using Grand.Module.Api.DTOs.Common; +using Grand.Module.Api.DTOs.Customers; +using Grand.Module.Api.Infrastructure.Mapper.Profiles; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; +using AddressProfile = Grand.Module.Api.Infrastructure.Mapper.Profiles.AddressProfile; +using BrandProfile = Grand.Module.Api.Infrastructure.Mapper.Profiles.BrandProfile; +using CategoryProfile = Grand.Module.Api.Infrastructure.Mapper.Profiles.CategoryProfile; +using CollectionProfile = Grand.Module.Api.Infrastructure.Mapper.Profiles.CollectionProfile; +using SpecificationAttributeProfile = Grand.Module.Api.Infrastructure.Mapper.Profiles.SpecificationAttributeProfile; +using TierPriceProfile = Grand.Module.Api.Infrastructure.Mapper.Profiles.TierPriceProfile; + +namespace Grand.Mapping.Tests.Api; + +[TestClass] +public class ApiMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── Address ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task AddressDto_ToEntity() + { + var dto = new AddressDto { + FirstName = "John", + LastName = "Doe", + Email = "john@example.com", + PhoneNumber = "123-456-7890", + Address1 = "123 Main St", + City = "Springfield", + ZipPostalCode = "12345", + CountryId = "US" + }; + return Verify(_mapper.Map
(dto)); + } + + [TestMethod] + public Task Address_ToDto() + { + var entity = new Address { + Id = "addr-1", + FirstName = "John", + LastName = "Doe", + Email = "john@example.com", + PhoneNumber = "123-456-7890", + Address1 = "123 Main St", + City = "Springfield", + ZipPostalCode = "12345", + CountryId = "US" + }; + return Verify(_mapper.Map(entity)); + } + + // ── Brand ───────────────────────────────────────────────────────────────── + + [TestMethod] + public Task BrandDto_ToEntity() + { + var dto = new BrandDto { + Id = "brand-1", + Name = "Nike", + SeName = "nike", + Description = "Sports brand", + MetaTitle = "Nike", + PageSize = 12, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24", + ShowOnHomePage = true, + Published = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task Brand_ToDto() + { + var entity = new Brand { + Id = "brand-1", + Name = "Nike", + SeName = "nike", + Description = "Sports brand", + MetaTitle = "Nike", + PageSize = 12, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24", + ShowOnHomePage = true, + Published = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(entity)); + } + + // ── Category ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task CategoryDto_ToEntity() + { + var dto = new CategoryDto { + Id = "cat-1", + Name = "Electronics", + SeName = "electronics", + Description = "Electronic items", + MetaTitle = "Electronics", + PageSize = 9, + AllowCustomersToSelectPageSize = true, + Published = true, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task Category_ToDto() + { + var entity = new Category { + Id = "cat-1", + Name = "Electronics", + SeName = "electronics", + Description = "Electronic items", + MetaTitle = "Electronics", + PageSize = 9, + AllowCustomersToSelectPageSize = true, + Published = true, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(entity)); + } + + // ── Collection ──────────────────────────────────────────────────────────── + + [TestMethod] + public Task CollectionDto_ToEntity() + { + var dto = new CollectionDto { + Id = "col-1", + Name = "Summer 2025", + SeName = "summer-2025", + Description = "Summer collection", + PageSize = 12, + Published = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task Collection_ToDto() + { + var entity = new Collection { + Id = "col-1", + Name = "Summer 2025", + SeName = "summer-2025", + Description = "Summer collection", + PageSize = 12, + Published = true, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(entity)); + } + + // ── CustomerGroup ───────────────────────────────────────────────────────── + + [TestMethod] + public Task CustomerGroupDto_ToEntity() + { + var dto = new CustomerGroupDto { + Id = "cg-1", + Name = "VIP Customers", + SystemName = "Vip", + FreeShipping = true + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task CustomerGroup_ToDto() + { + var entity = new CustomerGroup { + Id = "cg-1", + Name = "VIP Customers", + SystemName = "Vip", + FreeShipping = true + }; + return Verify(_mapper.Map(entity)); + } + + // ── Customer ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task CustomerDto_ToEntity() + { + var dto = new CustomerDto { + Id = "cust-1", + Email = "customer@example.com", + Username = "customer1", + Active = true + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task Customer_ToDto_EmptyUserFields() + { + var entity = new Customer { + Id = "cust-1", + Email = "customer@example.com", + Username = "customer1", + Active = true + }; + return Verify(_mapper.Map(entity)); + } + + // ── Picture ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task PictureDto_ToEntity() + { + var dto = new PictureDto { + Id = "pic-1", + MimeType = "image/jpeg", + SeoFilename = "product-image", + AltAttribute = "Product image", + TitleAttribute = "Product" + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task Picture_ToDto() + { + var entity = new Picture { + Id = "pic-1", + MimeType = "image/jpeg", + SeoFilename = "product-image", + AltAttribute = "Product image", + TitleAttribute = "Product" + }; + return Verify(_mapper.Map(entity)); + } + + // ── ProductAttribute ────────────────────────────────────────────────────── + + [TestMethod] + public Task ProductAttributeDto_ToEntity() + { + var dto = new ProductAttributeDto { + Id = "pa-1", + Name = "Color", + Description = "Product color" + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task ProductAttribute_ToDto() + { + var entity = new ProductAttribute { + Id = "pa-1", + Name = "Color", + Description = "Product color" + }; + return Verify(_mapper.Map(entity)); + } + + [TestMethod] + public Task PredefinedProductAttributeValue_ToDto() + { + var entity = new PredefinedProductAttributeValue { + Id = "ppav-1", + Name = "Red", + PriceAdjustment = 0, + WeightAdjustment = 0, + IsPreSelected = false, + DisplayOrder = 1 + }; + return Verify(_mapper.Map(entity)); + } + + // ── ProductAttributeMapping ─────────────────────────────────────────────── + + [TestMethod] + public Task ProductAttributeMappingDto_ToEntity() + { + var dto = new ProductAttributeMappingDto { + Id = "pam-1", + ProductAttributeId = "pa-1", + TextPrompt = "Choose color", + IsRequired = true, + AttributeControlTypeId = (AttributeControlType)1, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task ProductAttributeMapping_ToDto() + { + var entity = new ProductAttributeMapping { + Id = "pam-1", + ProductAttributeId = "pa-1", + TextPrompt = "Choose color", + IsRequired = true, + AttributeControlTypeId = (AttributeControlType)1, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(entity)); + } + + // ── SpecificationAttribute ──────────────────────────────────────────────── + + [TestMethod] + public Task SpecificationAttributeDto_ToEntity() + { + var dto = new SpecificationAttributeDto { + Id = "sa-1", + Name = "Material", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task SpecificationAttribute_ToDto() + { + var entity = new SpecificationAttribute { + Id = "sa-1", + Name = "Material", + DisplayOrder = 1 + }; + return Verify(_mapper.Map(entity)); + } + + [TestMethod] + public Task SpecificationAttributeOption_ToDto() + { + var entity = new SpecificationAttributeOption { + Id = "sao-1", + Name = "Cotton", + DisplayOrder = 0, + ColorSquaresRgb = "#FFFFFF" + }; + return Verify(_mapper.Map(entity)); + } + + // ── TierPrice ───────────────────────────────────────────────────────────── + + [TestMethod] + public Task ProductTierPriceDto_ToEntity() + { + var dto = new ProductTierPriceDto { + Id = "tp-1", + StoreId = "", + CustomerGroupId = "", + Quantity = 10, + Price = 9.99, + StartDateTimeUtc = null, + EndDateTimeUtc = null + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task TierPrice_ToDto() + { + var entity = new TierPrice { + Id = "tp-1", + StoreId = "", + CustomerGroupId = "", + Quantity = 10, + Price = 9.99 + }; + return Verify(_mapper.Map(entity)); + } + + // ── Product ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task ProductDto_ToEntity() + { + var dto = new ProductDto { + Id = "prod-1", + Name = "Test Product", + ShortDescription = "Short", + FullDescription = "Full description", + Sku = "SKU001", + Price = 29.99, + Published = true, + StockQuantity = 100, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task Product_ToDto() + { + var entity = new Product { + Id = "prod-1", + Name = "Test Product", + ShortDescription = "Short", + FullDescription = "Full description", + Sku = "SKU001", + Price = 29.99, + Published = true, + StockQuantity = 100, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(entity)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_NullConditions_NotApplied.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_NullConditions_NotApplied.verified.txt new file mode 100644 index 000000000..cc56a03d3 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_NullConditions_NotApplied.verified.txt @@ -0,0 +1,12 @@ +{ + Name: Partial Brand, + AllowCustomersToSelectPageSize: false, + ShowOnHomePage: false, + IncludeInMenu: false, + DefaultSort: -1, + Published: false, + LimitedToGroups: false, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_WithNullableValues.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_WithNullableValues.verified.txt new file mode 100644 index 000000000..6926b2deb --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.BrandDto_ToEntity_WithNullableValues.verified.txt @@ -0,0 +1,19 @@ +{ + Name: Nike, + Description: Sports brand, + BrandLayoutId: layout-1, + MetaTitle: Nike, + PageSize: 12, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + ShowOnHomePage: true, + IncludeInMenu: true, + ExternalId: ext-001, + Published: true, + DisplayOrder: 1, + LimitedToGroups: false, + SeName: nike, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: brand-export-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_NullConditions_NotApplied.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_NullConditions_NotApplied.verified.txt new file mode 100644 index 000000000..3277377dd --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_NullConditions_NotApplied.verified.txt @@ -0,0 +1,15 @@ +{ + Name: Partial Category, + AllowCustomersToSelectPageSize: false, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + ShowOnSearchBox: false, + IncludeInMenu: false, + Published: false, + DefaultSort: -1, + HideOnCatalog: false, + LimitedToGroups: false, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_WithNullableValues.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_WithNullableValues.verified.txt new file mode 100644 index 000000000..243d5b00c --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CategoryDto_ToEntity_WithNullableValues.verified.txt @@ -0,0 +1,23 @@ +{ + Name: Electronics, + Description: Electronic products, + CategoryLayoutId: layout-1, + MetaKeywords: electronics, gadgets, + MetaDescription: Best electronics, + MetaTitle: Electronics, + ParentCategoryId: , + PageSize: 12, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + ShowOnHomePage: true, + FeaturedProductsOnHomePage: false, + ShowOnSearchBox: false, + IncludeInMenu: true, + Published: true, + HideOnCatalog: false, + LimitedToGroups: false, + SeName: electronics, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: cat-export-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_NullConditions_NotApplied.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_NullConditions_NotApplied.verified.txt new file mode 100644 index 000000000..e6b76031d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_NullConditions_NotApplied.verified.txt @@ -0,0 +1,13 @@ +{ + Name: Partial Collection, + AllowCustomersToSelectPageSize: false, + ShowOnHomePage: false, + FeaturedProductsOnHomePage: false, + IncludeInMenu: false, + DefaultSort: -1, + Published: false, + LimitedToGroups: false, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_WithNullableValues.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_WithNullableValues.verified.txt new file mode 100644 index 000000000..859e6f611 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.CollectionDto_ToEntity_WithNullableValues.verified.txt @@ -0,0 +1,20 @@ +{ + Name: Summer 2025, + Description: Summer items, + CollectionLayoutId: layout-1, + MetaTitle: Summer 2025, + PageSize: 12, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + ShowOnHomePage: true, + FeaturedProductsOnHomePage: false, + IncludeInMenu: true, + Published: true, + DisplayOrder: 3, + ExternalId: ext-col-001, + LimitedToGroups: false, + SeName: summer-2025, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: col-export-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_BasicProperties.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_BasicProperties.verified.txt new file mode 100644 index 000000000..cf7ca9597 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_BasicProperties.verified.txt @@ -0,0 +1,45 @@ +{ + VisibleIndividually: true, + Name: Test Product, + ShortDescription: Short, + FullDescription: Full description, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + Sku: SKU001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: false, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockQuantity: 100, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 29.99, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + AuctionEnded: false, + Published: true, + LimitedToGroups: false, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: prod-export-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_ExistingIdPreservedWhenEmpty.verified.txt b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_ExistingIdPreservedWhenEmpty.verified.txt new file mode 100644 index 000000000..52885385b --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.ProductDto_ToEntity_ExistingIdPreservedWhenEmpty.verified.txt @@ -0,0 +1,40 @@ +{ + VisibleIndividually: false, + Name: No Id Product, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: false, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + AuctionEnded: false, + Published: false, + LimitedToGroups: false, + LimitedToStores: false, + UpdatedOnUtc: DateTime_1, + Id: existing-id +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs new file mode 100644 index 000000000..f5823ced3 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs @@ -0,0 +1,177 @@ +using AutoMapper; +using Grand.Business.Catalog.Services.ExportImport.Mapper; +using Grand.Business.Core.Dto; +using Grand.Domain.Catalog; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; + +namespace Grand.Mapping.Tests.Catalog; + +/// +/// Tests for ExportImport mapping profiles. +/// NOTE: These profiles set UpdatedOnUtc = DateTime.UtcNow via MapFrom. +/// Verify automatically scrubs DateTime values, so snapshots remain stable. +/// +[TestClass] +public class ExportImportMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── Category ────────────────────────────────────────────────────────────── + + [TestMethod] + public Task CategoryDto_ToEntity_WithNullableValues() + { + var dto = new CategoryDto { + Id = "cat-export-1", + Name = "Electronics", + SeName = "electronics", + Description = "Electronic products", + MetaTitle = "Electronics", + MetaKeywords = "electronics, gadgets", + MetaDescription = "Best electronics", + CategoryLayoutId = "layout-1", + ParentCategoryId = "", + PageSize = 12, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24", + ShowOnHomePage = true, + FeaturedProductsOnHomePage = false, + ShowOnSearchBox = false, + SearchBoxDisplayOrder = 0, + IncludeInMenu = true, + DefaultSort = 0, + HideOnCatalog = false, + Published = true, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task CategoryDto_ToEntity_NullConditions_NotApplied() + { + // When nullable fields are null, the mapping condition is false, so + // the destination keeps its default values for those members. + var dto = new CategoryDto { + Name = "Partial Category" + // All nullable properties are null → conditions false → not mapped + }; + return Verify(_mapper.Map(dto)); + } + + // ── Brand ───────────────────────────────────────────────────────────────── + + [TestMethod] + public Task BrandDto_ToEntity_WithNullableValues() + { + var dto = new BrandDto { + Id = "brand-export-1", + Name = "Nike", + SeName = "nike", + Description = "Sports brand", + MetaTitle = "Nike", + BrandLayoutId = "layout-1", + PageSize = 12, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24", + ShowOnHomePage = true, + IncludeInMenu = true, + DefaultSort = 0, + Published = true, + DisplayOrder = 1, + ExternalId = "ext-001" + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task BrandDto_ToEntity_NullConditions_NotApplied() + { + var dto = new BrandDto { + Name = "Partial Brand" + }; + return Verify(_mapper.Map(dto)); + } + + // ── Collection ──────────────────────────────────────────────────────────── + + [TestMethod] + public Task CollectionDto_ToEntity_WithNullableValues() + { + var dto = new CollectionDto { + Id = "col-export-1", + Name = "Summer 2025", + SeName = "summer-2025", + Description = "Summer items", + MetaTitle = "Summer 2025", + CollectionLayoutId = "layout-1", + PageSize = 12, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24", + ShowOnHomePage = true, + FeaturedProductsOnHomePage = false, + IncludeInMenu = true, + DefaultSort = 0, + Published = true, + DisplayOrder = 3, + ExternalId = "ext-col-001" + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task CollectionDto_ToEntity_NullConditions_NotApplied() + { + var dto = new CollectionDto { + Name = "Partial Collection" + }; + return Verify(_mapper.Map(dto)); + } + + // ── Product ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task ProductDto_ToEntity_BasicProperties() + { + // ProductProfile has many nullable conditions; test with a minimal set + var dto = new Grand.Business.Core.Dto.ProductDto { + Id = "prod-export-1", + Name = "Test Product", + ShortDescription = "Short", + FullDescription = "Full description", + Sku = "SKU001", + Published = true, + VisibleIndividually = true, + ShowOnHomePage = false, + Price = 29.99, + StockQuantity = 100, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(dto)); + } + + [TestMethod] + public Task ProductDto_ToEntity_ExistingIdPreservedWhenEmpty() + { + // When Id is null/empty, the Condition prevents overwriting the existing Id. + var dto = new Grand.Business.Core.Dto.ProductDto { + Id = "", // empty → condition false → Id not overwritten + Name = "No Id Product" + }; + var destination = new Product { Id = "existing-id" }; + return Verify(_mapper.Map(dto, destination)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/Grand.Mapping.Tests.csproj b/src/Tests/Grand.Mapping.Tests/Grand.Mapping.Tests.csproj new file mode 100644 index 000000000..83d91662e --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Grand.Mapping.Tests.csproj @@ -0,0 +1,27 @@ + + + + + false + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Address_ToVendorAddressModel.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Address_ToVendorAddressModel.verified.txt new file mode 100644 index 000000000..8401f5b8b --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Address_ToVendorAddressModel.verified.txt @@ -0,0 +1,38 @@ +{ + FirstName: Jane, + LastName: Smith, + Email: jane@vendor.com, + CountryId: GB, + City: Tradeville, + Address1: 42 Commerce St, + ZipPostalCode: 99001, + PhoneNumber: 555-1234, + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + Id: addr-v-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt new file mode 100644 index 000000000..ffbf7d904 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt @@ -0,0 +1,9 @@ +{ + Id: pac-v-1, + StockQuantity: 5, + AllowOutOfStockOrders: false, + Sku: COMB-SKU, + NotifyAdminForQuantityBelow: 1, + Attributes: System.Collections.Generic.List`1[Grand.Domain.Common.CustomAttribute], + UseMultipleWarehouses: false +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeMapping_ToVendorModel.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeMapping_ToVendorModel.verified.txt new file mode 100644 index 000000000..2e5ae0370 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeMapping_ToVendorModel.verified.txt @@ -0,0 +1,12 @@ +{ + ProductAttributeId: pa-1, + TextPrompt: Choose size, + IsRequired: true, + ShowOnCatalogPage: false, + AttributeControlTypeId: DropdownList, + Combination: false, + ShouldHaveValues: false, + ValidationRulesAllowed: false, + ConditionAllowed: false, + Id: pam-v-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Product_ToVendorProductModel.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Product_ToVendorProductModel.verified.txt new file mode 100644 index 000000000..b7826ea54 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Product_ToVendorProductModel.verified.txt @@ -0,0 +1,57 @@ +{ + Id: prod-v-1, + ProductTypeId: 5, + AuctionEnded: false, + VisibleIndividually: false, + Name: Vendor Product, + ShortDescription: Short desc, + FullDescription: Full description, + AllowCustomerReviews: false, + Sku: VSKU001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsRecurring: false, + CalendarModel: { + Interval: 1, + IncBothDate: false, + Quantity: 1, + Monday: false, + Tuesday: false, + Wednesday: false, + Thursday: false, + Friday: false, + Saturday: false, + Sunday: false + }, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockQuantity: 50, + StockAvailability: false, + DisplayStockQuantity: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 49.99, + OldPrice: 59.99, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + Weight: 1.5, + DisplayOrder: 1, + Published: true, + AddPictureModel: { + IsDefault: false + }, + CopyProductModel: { + CopyImages: false, + Published: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorModel_ToVendor.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorModel_ToVendor.verified.txt new file mode 100644 index 000000000..77b3c9e2d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorModel_ToVendor.verified.txt @@ -0,0 +1,17 @@ +{ + Name: Updated Vendor, + Email: updated@example.com, + Description: Updated description, + Active: false, + Deleted: false, + MetaKeywords: vendor, updated, + MetaTitle: Updated Vendor, + PageSize: 12, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + AllowCustomerReviews: false, + Address: { + Id: ObjectId_1 + }, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorProductModel_ToProduct.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorProductModel_ToProduct.verified.txt new file mode 100644 index 000000000..0cb803d5f --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.VendorProductModel_ToProduct.verified.txt @@ -0,0 +1,44 @@ +{ + VisibleIndividually: false, + Name: New Product, + ShortDescription: Short, + FullDescription: Full, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + Sku: SKU002, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: false, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockQuantity: 25, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + Price: 39.99, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + AuctionEnded: false, + Published: true, + LimitedToGroups: false, + LimitedToStores: false, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Vendor_ToVendorModel.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Vendor_ToVendorModel.verified.txt new file mode 100644 index 000000000..5a8dad417 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.Vendor_ToVendorModel.verified.txt @@ -0,0 +1,43 @@ +{ + Name: Test Vendor, + Email: vendor@example.com, + Description: Quality products, + MetaKeywords: vendor, test, + MetaDescription: Best vendor, + MetaTitle: Test Vendor, + SeName: test-vendor, + PageSize: 6, + AllowCustomersToSelectPageSize: true, + PageSizeOptions: 6, 12, 24, + Address: { + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + Id: ObjectId_1 + }, + Id: vendor-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs new file mode 100644 index 000000000..569ee2549 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs @@ -0,0 +1,160 @@ +using AutoMapper; +using Grand.Domain.Catalog; +using Grand.Domain.Common; +using Grand.Web.Vendor.Mapper; +using Grand.Web.Vendor.Models.Catalog; +using Grand.Web.Vendor.Models.Common; +using Grand.Web.Vendor.Models.Vendor; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VerifyMSTest; +using AddressProfile = Grand.Web.Vendor.Mapper.AddressProfile; +using ProductProfile = Grand.Web.Vendor.Mapper.ProductProfile; +using VendorProfile = Grand.Web.Vendor.Mapper.VendorProfile; + +namespace Grand.Mapping.Tests.Vendor; + +[TestClass] +public class VendorMappingTests : VerifyBase +{ + private IMapper _mapper; + + [TestInitialize] + public void Setup() + { + var config = new MapperConfiguration(cfg => { + cfg.AddProfile(); + cfg.AddProfile(); + cfg.AddProfile(); + }); + _mapper = config.CreateMapper(); + } + + // ── Address ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Address_ToVendorAddressModel() + { + var entity = new Address { + Id = "addr-v-1", + FirstName = "Jane", + LastName = "Smith", + Email = "jane@vendor.com", + PhoneNumber = "555-1234", + Address1 = "42 Commerce St", + City = "Tradeville", + ZipPostalCode = "99001", + CountryId = "GB" + }; + return Verify(_mapper.Map(entity)); + } + + // ── Vendor ──────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Vendor_ToVendorModel() + { + var entity = new Grand.Domain.Vendors.Vendor { + Id = "vendor-1", + Name = "Test Vendor", + SeName = "test-vendor", + Description = "Quality products", + Email = "vendor@example.com", + MetaTitle = "Test Vendor", + MetaKeywords = "vendor, test", + MetaDescription = "Best vendor", + Active = true, + Deleted = false, + DisplayOrder = 0, + PageSize = 6, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24", + AllowCustomerReviews = true + }; + return Verify(_mapper.Map(entity)); + } + + [TestMethod] + public Task VendorModel_ToVendor() + { + var model = new VendorModel { + Name = "Updated Vendor", + Description = "Updated description", + Email = "updated@example.com", + MetaTitle = "Updated Vendor", + MetaKeywords = "vendor, updated", + PageSize = 12, + AllowCustomersToSelectPageSize = true, + PageSizeOptions = "6, 12, 24" + }; + return Verify(_mapper.Map(model)); + } + + // ── Product ─────────────────────────────────────────────────────────────── + + [TestMethod] + public Task Product_ToVendorProductModel() + { + var entity = new Product { + Id = "prod-v-1", + Name = "Vendor Product", + ShortDescription = "Short desc", + FullDescription = "Full description", + Sku = "VSKU001", + Price = 49.99, + OldPrice = 59.99, + Published = true, + StockQuantity = 50, + Weight = 1.5, + DisplayOrder = 1, + ProductTypeId = (ProductType)5 + }; + return Verify(_mapper.Map(entity)); + } + + [TestMethod] + public Task VendorProductModel_ToProduct() + { + var model = new ProductModel { + Name = "New Product", + ShortDescription = "Short", + FullDescription = "Full", + Sku = "SKU002", + Price = 39.99, + Published = true, + StockQuantity = 25 + }; + return Verify(_mapper.Map(model)); + } + + // ── ProductAttributeMapping ─────────────────────────────────────────────── + + [TestMethod] + public Task ProductAttributeMapping_ToVendorModel() + { + var entity = new ProductAttributeMapping { + Id = "pam-v-1", + ProductAttributeId = "pa-1", + TextPrompt = "Choose size", + IsRequired = true, + AttributeControlTypeId = (AttributeControlType)1, + DisplayOrder = 0 + }; + return Verify(_mapper.Map(entity)); + } + + // ── ProductAttributeCombination ─────────────────────────────────────────── + + [TestMethod] + public Task ProductAttributeCombination_ToVendorModel() + { + var entity = new ProductAttributeCombination { + Id = "pac-v-1", + Sku = "COMB-SKU", + StockQuantity = 5, + AllowOutOfStockOrders = false, + OverriddenPrice = null, + NotifyAdminForQuantityBelow = 1 + }; + return Verify(_mapper.Map(entity)); + } +} diff --git a/src/Tests/Grand.Mapping.Tests/VerifyConfig.cs b/src/Tests/Grand.Mapping.Tests/VerifyConfig.cs new file mode 100644 index 000000000..9f5225eb3 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/VerifyConfig.cs @@ -0,0 +1,33 @@ +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; + +namespace Grand.Mapping.Tests; + +public static class VerifyConfig +{ + private static readonly Regex ObjectIdPattern = new(@"\b[0-9a-f]{24}\b", RegexOptions.Compiled); + + [ModuleInitializer] + public static void Initialize() + { + // Replace auto-generated MongoDB ObjectIds with deterministic placeholders, + // the same way Verify.ScrubInlineGuids() works for standard GUIDs. + VerifierSettings.AddScrubber((sb) => + { + var seen = new Dictionary(); + var counter = 0; + var result = ObjectIdPattern.Replace(sb.ToString(), m => + { + if (!seen.TryGetValue(m.Value, out var label)) + { + label = $"ObjectId_{++counter}"; + seen[m.Value] = label; + } + return label; + }); + sb.Clear(); + sb.Append(result); + }); + } +} From 8c9bd306806906880c902ff7ac0996e985ef40b1 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Fri, 27 Mar 2026 19:22:21 +0100 Subject: [PATCH 05/16] Add more tests --- GrandNode.sln | 9 +-- ...del_ToDomain_WithCalendarData.verified.txt | 40 +++++++++++++ ...duct_ToModel_WithCalendarData.verified.txt | 55 ++++++++++++++++++ .../AdminShared/CatalogProductMappingTests.cs | 25 ++++++++ ..._ToDomain_WithAddressSettings.verified.txt | 57 +++++++++++++++++++ ...s_ToModel_WithAddressSettings.verified.txt | 35 ++++++++++++ .../AdminShared/SystemMappingTests.cs | 52 +++++++++++++++++ ...Customer_ToDto_WithUserFields.verified.txt | 24 ++++++++ .../Api/ApiMappingTests.cs | 28 +++++++++ 9 files changed, 319 insertions(+), 6 deletions(-) create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain_WithCalendarData.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel_WithCalendarData.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain_WithAddressSettings.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel_WithAddressSettings.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_WithUserFields.verified.txt diff --git a/GrandNode.sln b/GrandNode.sln index 2fe76ecdd..181fcf07e 100644 --- a/GrandNode.sln +++ b/GrandNode.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32014.148 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11619.145 insiders MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{FA350BD6-C29D-40D9-BA47-FE5FBDFC84A0}" EndProject @@ -147,8 +147,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grand.Web.AdminShared", "sr EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CEA09484-30F6-4D44-02F6-822E06DBC57C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grand.Mapping.Tests", "src\Tests\Grand.Mapping.Tests\Grand.Mapping.Tests.csproj", "{396E6929-5365-4116-8AA8-DF5327247798}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{03997797-E7F5-0643-168D-B8EA7178C2FE}" @@ -950,8 +948,7 @@ Global {E0B26803-010B-4198-8016-002242076B61} = {583FFBBD-421C-4FB7-BBDA-F9CAF35A354A} {136F1E35-8B20-465C-8D42-30A5A0D0DA1F} = {583FFBBD-421C-4FB7-BBDA-F9CAF35A354A} {12C4A556-E62E-4EC0-BCD9-E9D4E1F3D430} = {BF4543A8-0731-4FDD-BB6B-0ADB9561F981} - {CEA09484-30F6-4D44-02F6-822E06DBC57C} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {396E6929-5365-4116-8AA8-DF5327247798} = {CEA09484-30F6-4D44-02F6-822E06DBC57C} + {396E6929-5365-4116-8AA8-DF5327247798} = {6360202A-F931-4BBD-ADBD-C9A628EE59F8} {03997797-E7F5-0643-168D-B8EA7178C2FE} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} {EC447DCF-ABFA-6E24-52A5-D7FD48A5C558} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} {CC1F4FA2-92F2-BA4B-815B-7EC2CBBCCF50} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain_WithCalendarData.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain_WithCalendarData.verified.txt new file mode 100644 index 000000000..a9cb2d40d --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductModel_ToDomain_WithCalendarData.verified.txt @@ -0,0 +1,40 @@ +{ + VisibleIndividually: false, + Name: Calendar Product, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + Sku: CAL-001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + IncBothDate: true, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockAvailability: false, + DisplayStockQuantity: false, + LowStock: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + AuctionEnded: false, + Published: false, + LimitedToGroups: false, + LimitedToStores: false, + Id: ObjectId_1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel_WithCalendarData.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel_WithCalendarData.verified.txt new file mode 100644 index 000000000..7cd17a864 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.Product_ToModel_WithCalendarData.verified.txt @@ -0,0 +1,55 @@ +{ + Id: prod-002, + AuctionEnded: false, + VisibleIndividually: false, + Name: Calendar Product, + ShowOnHomePage: false, + BestSeller: false, + AllowCustomerReviews: false, + Sku: CAL-001, + IsGiftVoucher: false, + RequireOtherProducts: false, + AutoAddRequiredProducts: false, + IsDownload: false, + UnlimitedDownloads: false, + HasSampleDownload: false, + HasUserAgreement: false, + IsRecurring: false, + CalendarModel: { + Interval: 1, + IncBothDate: true, + Quantity: 1, + Monday: false, + Tuesday: false, + Wednesday: false, + Thursday: false, + Friday: false, + Saturday: false, + Sunday: false + }, + IsShipEnabled: false, + IsFreeShipping: false, + ShipSeparately: false, + IsTaxExempt: false, + IsTele: false, + UseMultipleWarehouses: false, + StockAvailability: false, + DisplayStockQuantity: false, + AllowOutOfStockSubscriptions: false, + NotReturnable: false, + DisableBuyButton: false, + DisableWishlistButton: false, + AvailableForPreOrder: false, + CallForPrice: false, + EnteredPrice: false, + BasepriceEnabled: false, + MarkAsNew: false, + Published: false, + AddPictureModel: { + IsDefault: false + }, + CopyProductModel: { + CopyImages: false, + Published: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs index a3127659e..3f7c4bcc7 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs @@ -93,6 +93,31 @@ public Task ProductModel_ToDomain() return Verify(_mapper.Map(model)); } + [TestMethod] + public Task Product_ToModel_WithCalendarData() + { + var source = new Product { + Id = "prod-002", + Name = "Calendar Product", + Sku = "CAL-001", + IncBothDate = true + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task ProductModel_ToDomain_WithCalendarData() + { + var model = new ProductModel { + Name = "Calendar Product", + Sku = "CAL-001", + CalendarModel = new ProductModel.GenerateCalendarModel { + IncBothDate = true + } + }; + return Verify(_mapper.Map(model)); + } + // ── TierPrice ───────────────────────────────────────────────────────────── [TestMethod] diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain_WithAddressSettings.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain_WithAddressSettings.verified.txt new file mode 100644 index 000000000..6e61b3d09 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettingsModel_ToDomain_WithAddressSettings.verified.txt @@ -0,0 +1,57 @@ +{ + VendorsBlockItemsToDisplay: 5, + ShowVendorOnProductDetailsPage: false, + AllowCustomersToContactVendors: false, + AllowCustomersToApplyForVendorAccount: false, + AllowSearchByVendor: false, + TermsOfServiceEnabled: false, + AllowVendorsToEditInfo: false, + NotifyStoreOwnerAboutVendorInformationChange: false, + VendorReviewsMustBeApproved: false, + AllowAnonymousUsersToReviewVendor: false, + VendorReviewPossibleOnlyAfterPurchasing: false, + NotifyVendorAboutNewVendorReviews: false, + DefaultAllowCustomerReview: false, + VendorReviewPossibleOnlyOnce: false, + CompanyEnabled: true, + CompanyRequired: true, + StreetAddressEnabled: true, + StreetAddressRequired: true, + StreetAddress2Enabled: true, + StreetAddress2Required: false, + ZipPostalCodeEnabled: true, + ZipPostalCodeRequired: false, + CityEnabled: true, + CityRequired: false, + CountryEnabled: true, + StateProvinceEnabled: true, + PhoneEnabled: true, + PhoneRequired: false, + FaxEnabled: false, + NoteEnabled: false, + FaxRequired: false, + AddressSettings: { + NameEnabled: false, + CompanyEnabled: true, + CompanyRequired: true, + VatNumberEnabled: false, + VatNumberRequired: false, + StreetAddressEnabled: true, + StreetAddressRequired: true, + StreetAddress2Enabled: true, + StreetAddress2Required: false, + ZipPostalCodeEnabled: true, + ZipPostalCodeRequired: false, + CityEnabled: true, + CityRequired: false, + CountryEnabled: true, + StateProvinceEnabled: true, + PhoneEnabled: true, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false, + DisallowUsersToChangeEmail: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel_WithAddressSettings.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel_WithAddressSettings.verified.txt new file mode 100644 index 000000000..ab0cac556 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.VendorSettings_ToModel_WithAddressSettings.verified.txt @@ -0,0 +1,35 @@ +{ + VendorsBlockItemsToDisplay: 5, + ShowVendorOnProductDetailsPage: false, + AllowCustomersToContactVendors: false, + AllowCustomersToApplyForVendorAccount: false, + AllowSearchByVendor: false, + AllowVendorsToEditInfo: false, + NotifyStoreOwnerAboutVendorInformationChange: false, + TermsOfServiceEnabled: false, + VendorReviewsMustBeApproved: false, + AllowAnonymousUsersToReviewVendor: false, + VendorReviewPossibleOnlyAfterPurchasing: false, + NotifyVendorAboutNewVendorReviews: false, + DefaultAllowCustomerReview: false, + NumberOfReview: 10, + VendorReviewPossibleOnlyOnce: false, + AddressSettings: { + CompanyEnabled: true, + CompanyRequired: true, + StreetAddressEnabled: true, + StreetAddressRequired: true, + StreetAddress2Enabled: true, + StreetAddress2Required: false, + ZipPostalCodeEnabled: true, + ZipPostalCodeRequired: false, + CityEnabled: true, + CityRequired: false, + CountryEnabled: true, + StateProvinceEnabled: true, + PhoneEnabled: true, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false + } +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs index 5172e53ae..1b271d7f4 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs @@ -284,4 +284,56 @@ public Task VendorSettingsModel_ToDomain() }; return Verify(_mapper.Map(model)); } + + [TestMethod] + public Task VendorSettings_ToModel_WithAddressSettings() + { + var source = new VendorSettings { + VendorsBlockItemsToDisplay = 5, + CityEnabled = true, + CityRequired = false, + CompanyEnabled = true, + CompanyRequired = true, + CountryEnabled = true, + FaxEnabled = false, + FaxRequired = false, + PhoneEnabled = true, + PhoneRequired = false, + StateProvinceEnabled = true, + StreetAddress2Enabled = true, + StreetAddress2Required = false, + StreetAddressEnabled = true, + StreetAddressRequired = true, + ZipPostalCodeEnabled = true, + ZipPostalCodeRequired = false + }; + return Verify(_mapper.Map(source)); + } + + [TestMethod] + public Task VendorSettingsModel_ToDomain_WithAddressSettings() + { + var model = new VendorSettingsModel { + VendorsBlockItemsToDisplay = 5, + AddressSettings = new VendorSettingsModel.AddressSettingsModel { + CityEnabled = true, + CityRequired = false, + CompanyEnabled = true, + CompanyRequired = true, + CountryEnabled = true, + FaxEnabled = false, + FaxRequired = false, + PhoneEnabled = true, + PhoneRequired = false, + StateProvinceEnabled = true, + StreetAddress2Enabled = true, + StreetAddress2Required = false, + StreetAddressEnabled = true, + StreetAddressRequired = true, + ZipPostalCodeEnabled = true, + ZipPostalCodeRequired = false + } + }; + return Verify(_mapper.Map(model)); + } } diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_WithUserFields.verified.txt b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_WithUserFields.verified.txt new file mode 100644 index 000000000..bd469883a --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.Customer_ToDto_WithUserFields.verified.txt @@ -0,0 +1,24 @@ +{ + CustomerGuid: Guid_1, + Username: customer1, + Email: customer@example.com, + IsTaxExempt: false, + FreeShipping: false, + Active: true, + Deleted: false, + Gender: M, + FirstName: John, + LastName: Doe, + Company: Acme Corp, + StreetAddress: 123 Main St, + StreetAddress2: Apt 4, + ZipPostalCode: 10001, + City: New York, + CountryId: country-001, + StateProvinceId: state-001, + Phone: +1-555-0123, + Fax: +1-555-0124, + VatNumber: VAT123456, + VatNumberStatusId: 20, + Id: cust-1 +} \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs index df759febc..63dfdc789 100644 --- a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs @@ -238,6 +238,34 @@ public Task Customer_ToDto_EmptyUserFields() return Verify(_mapper.Map(entity)); } + [TestMethod] + public Task Customer_ToDto_WithUserFields() + { + var entity = new Customer { + Id = "cust-1", + Email = "customer@example.com", + Username = "customer1", + Active = true, + UserFields = [ + new UserField { Key = "FirstName", Value = "John", StoreId = "" }, + new UserField { Key = "LastName", Value = "Doe", StoreId = "" }, + new UserField { Key = "Gender", Value = "M", StoreId = "" }, + new UserField { Key = "City", Value = "New York", StoreId = "" }, + new UserField { Key = "Company", Value = "Acme Corp", StoreId = "" }, + new UserField { Key = "StreetAddress", Value = "123 Main St", StoreId = "" }, + new UserField { Key = "StreetAddress2", Value = "Apt 4", StoreId = "" }, + new UserField { Key = "ZipPostalCode", Value = "10001", StoreId = "" }, + new UserField { Key = "CountryId", Value = "country-001", StoreId = "" }, + new UserField { Key = "StateProvinceId", Value = "state-001", StoreId = "" }, + new UserField { Key = "Phone", Value = "+1-555-0123", StoreId = "" }, + new UserField { Key = "Fax", Value = "+1-555-0124", StoreId = "" }, + new UserField { Key = "VatNumber", Value = "VAT123456", StoreId = "" }, + new UserField { Key = "VatNumberStatusId", Value = "20", StoreId = "" } + ] + }; + return Verify(_mapper.Map(entity)); + } + // ── Picture ─────────────────────────────────────────────────────────────── [TestMethod] From 5f5d8bdd616374bf3a5c752bc8e4b13ca25f7387 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Fri, 27 Mar 2026 20:47:17 +0100 Subject: [PATCH 06/16] Commit --- .../Internal/IMappingConfiguration.cs | 2 +- .../Grand.Mapping/Internal/MappingCompiler.cs | 164 +++++++++++++++++- .../Internal/MappingConfiguration.cs | 4 +- .../Grand.Mapping/Internal/MemberConfig.cs | 2 + src/Core/Grand.Mapping/MapperConfiguration.cs | 12 +- .../AdminShared/CatalogLayoutMappingTests.cs | 2 +- .../AdminShared/CatalogMappingTests.cs | 2 +- ...tAttributeCombination_ToModel.verified.txt | 1 - .../AdminShared/CatalogProductMappingTests.cs | 2 +- ...pingTests.PickupPoint_ToModel.verified.txt | 29 ++++ ...pingTests.StoreModel_ToDomain.verified.txt | 3 + .../AdminShared/CommonMappingTests.cs | 2 +- .../AdminShared/ContentMappingTests.cs | 2 +- ...ewsLetterSubscription_ToModel.verified.txt | 1 - .../AdminShared/CustomerMappingTests.cs | 2 +- .../AdminShared/ProviderMappingTests.cs | 12 +- .../AdminShared/SettingsMappingTests.cs | 2 +- .../AdminShared/ShippingMappingTests.cs | 2 +- .../AdminShared/SystemMappingTests.cs | 2 +- .../Api/ApiMappingTests.cs | 2 +- .../Catalog/ExportImportMappingTests.cs | 2 +- .../Grand.Mapping.Tests/TestMappingHelpers.cs | 26 +++ ...buteCombination_ToVendorModel.verified.txt | 1 - .../Vendor/VendorMappingTests.cs | 2 +- 24 files changed, 247 insertions(+), 34 deletions(-) create mode 100644 src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs diff --git a/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs b/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs index ba144628d..67ee3c28e 100644 --- a/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs +++ b/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs @@ -3,5 +3,5 @@ namespace Grand.Mapping.Internal; internal interface IMappingConfiguration { (Type Source, Type Dest) GetTypes(); - Delegate CompileDelegate(); + Delegate CompileDelegate(Dictionary<(Type, Type), Delegate> mappings); } diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs index bb15ba8f1..a8a0c18c5 100644 --- a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.Linq.Expressions; using System.Reflection; @@ -5,7 +7,9 @@ namespace Grand.Mapping.Internal; internal static class MappingCompiler { - public static Action Compile(List configs) + public static Action Compile( + List configs, + Dictionary<(Type, Type), Delegate> mappings) { var src = Expression.Parameter(typeof(TSource), "src"); var dst = Expression.Parameter(typeof(TDest), "dst"); @@ -30,19 +34,38 @@ public static Action Compile(List : SourceProp(src, dp.Name); if (value == null) continue; - value = Coerce(value, dp.PropertyType); - if (value == null) continue; - var assign = Expression.Assign(destAccess, value); - body.Add(mc.ConditionExpression != null - ? Expression.IfThen(Expression.Invoke(mc.ConditionExpression, src), assign) - : (Expression)assign); + var coerced = Coerce(value, dp.PropertyType); + if (coerced != null) + { + var assign = Expression.Assign(destAccess, coerced); + body.Add(mc.ConditionExpression != null + ? Expression.IfThen(Expression.Invoke(mc.ConditionExpression, src), assign) + : (Expression)assign); + } + else + { + // Fallback: delegate cross-type mapping to a registered profile + var nested = BuildNestedMapping(value, destAccess, dp.PropertyType, + mc.ConditionExpression, src, mappings); + if (nested != null) body.Add(nested); + } } else { - var value = Coerce(SourceProp(src, dp.Name), dp.PropertyType); + var srcExpr = SourceProp(src, dp.Name); + if (srcExpr == null) continue; + + var value = Coerce(srcExpr, dp.PropertyType); if (value != null) body.Add(Expression.Assign(destAccess, value)); + else + { + // Fallback: delegate cross-type mapping to a registered profile + var nested = BuildNestedMapping(srcExpr, destAccess, dp.PropertyType, + null, src, mappings); + if (nested != null) body.Add(nested); + } } } @@ -67,6 +90,131 @@ public static Action Compile(List src, dst).Compile(); } + /// + /// Generates an expression that maps srcValueExpr → destAccess using a registered + /// profile mapping. Handles both single objects (A → B) and collections (IList<A> → IList<B>). + /// Returns null when no applicable registered mapping exists. + /// + private static Expression? BuildNestedMapping( + Expression srcValueExpr, + Expression destAccess, + Type destType, + LambdaExpression? condition, + ParameterExpression srcParam, + Dictionary<(Type, Type), Delegate> mappings) + { + var srcType = srcValueExpr.Type; + + // Case 1: A → B (both non-value, non-collection reference types with registered mapping) + if (!srcType.IsValueType && !destType.IsValueType + && CollectionElementType(srcType) == null + && CollectionElementType(destType) == null + && destType.GetConstructor(Type.EmptyTypes) != null + && mappings.ContainsKey((srcType, destType))) + { + // Capture the dictionary so the delegate can be looked up at runtime + // (when all delegates are guaranteed to be compiled). + var delConst = Expression.Constant(mappings); + var keyConst = Expression.Constant((srcType, destType)); + var getDel = Expression.Call( + delConst, + typeof(Dictionary<(Type, Type), Delegate>).GetMethod("get_Item")!, + keyConst); + var castDel = Expression.Convert( + getDel, + typeof(Action<,>).MakeGenericType(srcType, destType)); + + // Cache source value to avoid double-evaluation (e.g. when srcValueExpr is Invoke) + var srcVar = Expression.Variable(srcType, "ns"); + var tmpVar = Expression.Variable(destType, "nd"); + + var innerBlock = Expression.Block( + new[] { srcVar, tmpVar }, + Expression.Assign(srcVar, srcValueExpr), + Expression.IfThen( + Expression.ReferenceNotEqual(srcVar, Expression.Constant(null, srcType)), + Expression.Block( + Expression.Assign(tmpVar, Expression.New(destType)), + Expression.Invoke(castDel, srcVar, tmpVar), + Expression.Assign(destAccess, tmpVar)))); + + return condition != null + ? Expression.IfThen(Expression.Invoke(condition, srcParam), innerBlock) + : (Expression)innerBlock; + } + + // Case 2: IList → IList / A[] → B[] where A→B mapping is registered + var srcElem = CollectionElementType(srcType); + var dstElem = CollectionElementType(destType); + if (srcElem != null && dstElem != null && srcElem != dstElem + && mappings.ContainsKey((srcElem, dstElem))) + { + var converted = BuildCrossTypeCollectionCoerce(srcValueExpr, destType, srcElem, dstElem, mappings); + Expression assignExpr = Expression.Assign(destAccess, converted); + return condition != null + ? Expression.IfThen(Expression.Invoke(condition, srcParam), assignExpr) + : assignExpr; + } + + return null; + } + + private static Expression BuildCrossTypeCollectionCoerce( + Expression src, + Type destType, + Type srcElem, + Type dstElem, + Dictionary<(Type, Type), Delegate> mappings) + { + var delConst = Expression.Constant(mappings); + var keyConst = Expression.Constant((srcElem, dstElem)); + var getDel = Expression.Call( + delConst, + typeof(Dictionary<(Type, Type), Delegate>).GetMethod("get_Item")!, + keyConst); + var castDel = Expression.Convert(getDel, typeof(Action<,>).MakeGenericType(srcElem, dstElem)); + + // x => { var tmp = new DstElem(); del(x, tmp); return tmp; } + var xParam = Expression.Parameter(srcElem, "x"); + var tmpVar = Expression.Variable(dstElem, "tmp"); + var selectorBody = Expression.Block( + new[] { tmpVar }, + Expression.Assign(tmpVar, Expression.New(dstElem)), + Expression.Invoke(castDel, xParam, tmpVar), + tmpVar); + var selector = Expression.Lambda(selectorBody, xParam); + + var iEnumSrc = typeof(IEnumerable<>).MakeGenericType(srcElem); + var srcCast = src.Type == iEnumSrc ? src : Expression.Convert(src, iEnumSrc); + + var selectMethod = typeof(Enumerable) + .GetMethods() + .First(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2) + .MakeGenericMethod(srcElem, dstElem); + + Expression filled; + if (destType.IsArray) + { + var toArray = typeof(Enumerable).GetMethod(nameof(Enumerable.ToArray))!.MakeGenericMethod(dstElem); + filled = Expression.Call(toArray, Expression.Call(selectMethod, srcCast, selector)); + } + else + { + var toList = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList))!.MakeGenericMethod(dstElem); + filled = Expression.Call(toList, Expression.Call(selectMethod, srcCast, selector)); + } + + Expression emptyExpr = destType.IsArray + ? Expression.NewArrayBounds(dstElem, Expression.Constant(0)) + : (Expression)Expression.New(typeof(List<>).MakeGenericType(dstElem)); + + if (src.Type.IsValueType) return filled; + return Expression.Condition( + Expression.ReferenceEqual(src, Expression.Constant(null, src.Type)), + emptyExpr, + filled); + } + // Returns Expression for same-named readable property on src, or null. private static Expression? SourceProp(ParameterExpression src, string name) { diff --git a/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs b/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs index 645c23a3f..f7f742203 100644 --- a/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs +++ b/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs @@ -9,6 +9,6 @@ public MappingConfiguration(MappingExpressionImpl expression) public (Type Source, Type Dest) GetTypes() => (typeof(TSource), typeof(TDest)); - public Delegate CompileDelegate() - => MappingCompiler.Compile(_expression.MemberConfigs); + public Delegate CompileDelegate(Dictionary<(Type, Type), Delegate> mappings) + => MappingCompiler.Compile(_expression.MemberConfigs, mappings); } diff --git a/src/Core/Grand.Mapping/Internal/MemberConfig.cs b/src/Core/Grand.Mapping/Internal/MemberConfig.cs index f981b7b1a..71b12113e 100644 --- a/src/Core/Grand.Mapping/Internal/MemberConfig.cs +++ b/src/Core/Grand.Mapping/Internal/MemberConfig.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.Linq.Expressions; namespace Grand.Mapping.Internal; diff --git a/src/Core/Grand.Mapping/MapperConfiguration.cs b/src/Core/Grand.Mapping/MapperConfiguration.cs index b2e177e03..bf79c9018 100644 --- a/src/Core/Grand.Mapping/MapperConfiguration.cs +++ b/src/Core/Grand.Mapping/MapperConfiguration.cs @@ -10,10 +10,18 @@ public MapperConfiguration(Action configure) { var expr = new MapperConfigurationExpressionImpl(); configure(expr); - foreach (var config in expr.GetConfigurations()) + var configs = expr.GetConfigurations().ToList(); + + // First pass: register all type-pair keys so nested mappings can detect + // forward/cross references during compilation. + foreach (var config in configs) + _mappings[config.GetTypes()] = null!; + + // Second pass: compile all delegates (all keys are now registered). + foreach (var config in configs) { var key = config.GetTypes(); - _mappings[key] = config.CompileDelegate(); + _mappings[key] = config.CompileDelegate(_mappings); } } diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs index 99ba54da8..638a8bacd 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogLayoutMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Domain.Pages; using Grand.Web.AdminShared.Mapper; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs index acb07ad29..500b93cd6 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Domain.Discounts; using Grand.Domain.Orders; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt index 42831bb60..89b41ac76 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.ProductAttributeCombination_ToModel.verified.txt @@ -6,6 +6,5 @@ Gtin: 1234567890, OverriddenPrice: 95.0, NotifyAdminForQuantityBelow: 2, - Attributes: System.Collections.Generic.List`1[Grand.Domain.Common.CustomAttribute], UseMultipleWarehouses: false } \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs index 3f7c4bcc7..74aa98c2d 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogProductMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Web.AdminShared.Mapper; using Grand.Web.AdminShared.Models.Catalog; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt index 6a1ac4936..67f7da7bb 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.PickupPoint_ToModel.verified.txt @@ -2,6 +2,35 @@ Name: Main Store Pickup, Description: Pickup at main store, AdminComment: Note, + Address: { + NameEnabled: false, + FirstNameEnabled: false, + FirstNameRequired: false, + LastNameEnabled: false, + LastNameRequired: false, + EmailEnabled: false, + EmailRequired: false, + CompanyEnabled: false, + CompanyRequired: false, + VatNumberEnabled: false, + VatNumberRequired: false, + CountryEnabled: false, + StateProvinceEnabled: false, + CityEnabled: false, + CityRequired: false, + StreetAddressEnabled: false, + StreetAddressRequired: false, + StreetAddress2Enabled: false, + StreetAddress2Required: false, + ZipPostalCodeEnabled: false, + ZipPostalCodeRequired: false, + PhoneEnabled: false, + PhoneRequired: false, + FaxEnabled: false, + FaxRequired: false, + NoteEnabled: false, + AddressTypeEnabled: false + }, DisplayOrder: 1, StoreId: store-001, PickupFee: 2.0, diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt index 2fb7cfc05..28ae0fd58 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt @@ -5,5 +5,8 @@ DefaultLanguageId: lang-001, DisplayOrder: 1, CompanyName: Grand LLC, + BankAccount: { + Id: ObjectId_1 + }, Id: ObjectId_1 } \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs index db5474d07..19729d55f 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Common; using Grand.Domain.Directory; using Grand.Domain.Documents; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs index c75dce69d..3667332bf 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ContentMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Blogs; using Grand.Domain.Courses; using Grand.Domain.Knowledgebase; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt index 85961030c..0b962b977 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.NewsLetterSubscription_ToModel.verified.txt @@ -1,6 +1,5 @@ { Email: subscriber@example.com, Active: true, - Categories: System.Collections.Generic.List`1[System.String], Id: nls-001 } \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs index c2525e67d..edf927c7c 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CustomerMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Domain.Customers; using Grand.Domain.Messages; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs index ea6a4d401..7661e25fe 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ProviderMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Core.Interfaces.Authentication; using Grand.Business.Core.Interfaces.Catalog.Tax; using Grand.Business.Core.Interfaces.Checkout.Payments; @@ -47,7 +47,7 @@ public Task PaymentProvider_ToModel() mock.Setup(x => x.SystemName).Returns("Payments.CashOnDelivery"); mock.Setup(x => x.Priority).Returns(1); - return Verify(_mapper.Map(mock.Object)); + return Verify(_mapper.Map(mock.Object)); } // ── TaxProvider ─────────────────────────────────────────────────────────── @@ -59,7 +59,7 @@ public Task TaxProvider_ToModel() mock.Setup(x => x.FriendlyName).Returns("Fixed Rate"); mock.Setup(x => x.SystemName).Returns("Tax.FixedRate"); - return Verify(_mapper.Map(mock.Object)); + return Verify(_mapper.Map(mock.Object)); } // ── ShippingRateComputationMethod ───────────────────────────────────────── @@ -73,7 +73,7 @@ public Task ShippingRateCalculationProvider_ToModel() mock.Setup(x => x.Priority).Returns(5); mock.Setup(x => x.ConfigurationUrl).Returns("../ShippingFixedRate/Configure"); - return Verify(_mapper.Map(mock.Object)); + return Verify(_mapper.Map(mock.Object)); } // ── WidgetPlugin ────────────────────────────────────────────────────────── @@ -86,7 +86,7 @@ public Task WidgetProvider_ToModel() mock.Setup(x => x.SystemName).Returns("Widgets.Slider"); mock.Setup(x => x.Priority).Returns(0); - return Verify(_mapper.Map(mock.Object)); + return Verify(_mapper.Map(mock.Object)); } // ── ExternalAuthentication ──────────────────────────────────────────────── @@ -99,7 +99,7 @@ public Task ExternalAuthenticationProvider_ToModel() mock.Setup(x => x.SystemName).Returns("ExternalAuth.Facebook"); mock.Setup(x => x.Priority).Returns(0); - return Verify(_mapper.Map(mock.Object)); + return Verify(_mapper.Map(mock.Object)); } // ── PluginDescriptor ────────────────────────────────────────────────────── diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs index f9884f1a7..137bb0802 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SettingsMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Admin; using Grand.Domain.Blogs; using Grand.Domain.Catalog; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs index fb72d8e07..c974ea383 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/ShippingMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Domain.Orders; using Grand.Domain.Payments; diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs index 1b271d7f4..40f7b5704 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/SystemMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Admin; using Grand.Domain.Customers; using Grand.Domain.Messages; diff --git a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs index 63dfdc789..4ad226175 100644 --- a/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/Api/ApiMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Domain.Customers; diff --git a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs index f5823ced3..b5242524c 100644 --- a/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/Catalog/ExportImportMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Business.Catalog.Services.ExportImport.Mapper; using Grand.Business.Core.Dto; using Grand.Domain.Catalog; diff --git a/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs b/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs new file mode 100644 index 000000000..83e6ea445 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs @@ -0,0 +1,26 @@ +using Grand.Mapping; + +namespace Grand.Mapping.Tests; + +/// +/// Extension methods to preserve AutoMapper-style call syntax in tests +/// while using Grand.Mapping internally. +/// +internal static class TestMappingHelpers +{ + // Preserves: cfg.AddProfile() + public static void AddProfile(this IMapperConfigurationExpression cfg) + where T : Profile, new() + => cfg.AddProfile(new T()); + + // Preserves: _mapper.Map(source) + public static TDest Map(this IMapper mapper, object source) + { + if (source is null) return default!; + var mapMethod = typeof(IMapper) + .GetMethods() + .First(m => m.Name == nameof(IMapper.Map) && m.GetParameters().Length == 1) + .MakeGenericMethod(source.GetType(), typeof(TDest)); + return (TDest)mapMethod.Invoke(mapper, [source])!; + } +} diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt index ffbf7d904..8d4ae37e4 100644 --- a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.ProductAttributeCombination_ToVendorModel.verified.txt @@ -4,6 +4,5 @@ AllowOutOfStockOrders: false, Sku: COMB-SKU, NotifyAdminForQuantityBelow: 1, - Attributes: System.Collections.Generic.List`1[Grand.Domain.Common.CustomAttribute], UseMultipleWarehouses: false } \ No newline at end of file diff --git a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs index 569ee2549..da92f106f 100644 --- a/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/Vendor/VendorMappingTests.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using Grand.Mapping; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Web.Vendor.Mapper; From 64d181ce961f1d2a7f0290f551d911b28bce4647 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Fri, 27 Mar 2026 20:57:11 +0100 Subject: [PATCH 07/16] Commit --- src/Core/Grand.Infrastructure/StartupBase.cs | 2 +- src/Core/Grand.Mapping/GrandMapper.cs | 12 ++++++++++- .../Internal/ParameterReplacer.cs | 21 ------------------- 3 files changed, 12 insertions(+), 23 deletions(-) delete mode 100644 src/Core/Grand.Mapping/Internal/ParameterReplacer.cs diff --git a/src/Core/Grand.Infrastructure/StartupBase.cs b/src/Core/Grand.Infrastructure/StartupBase.cs index 4eb7c3344..34b6dc411 100644 --- a/src/Core/Grand.Infrastructure/StartupBase.cs +++ b/src/Core/Grand.Infrastructure/StartupBase.cs @@ -67,7 +67,7 @@ private static void InitAutoMapper(ITypeSearcher typeSearcher) //create AutoMapper configuration var config = new MapperConfiguration(cfg => { - foreach (var instance in instances) cfg.AddProfile(instance.GetType()); + foreach (var instance in instances) cfg.AddProfile((Grand.Mapping.Profile)instance); }); //register automapper diff --git a/src/Core/Grand.Mapping/GrandMapper.cs b/src/Core/Grand.Mapping/GrandMapper.cs index 0726b8030..94fdccb3c 100644 --- a/src/Core/Grand.Mapping/GrandMapper.cs +++ b/src/Core/Grand.Mapping/GrandMapper.cs @@ -1,3 +1,5 @@ +using System.Linq.Expressions; + namespace Grand.Mapping; internal sealed class GrandMapper : IMapper @@ -6,9 +8,17 @@ internal sealed class GrandMapper : IMapper internal GrandMapper(Dictionary<(Type, Type), Delegate> mappings) => _mappings = mappings; + // Caches a compiled parameterless constructor per TDest, avoiding Activator.CreateInstance + // reflection overhead on every Map call. + private static class ObjectFactory + { + internal static readonly Func Create = + Expression.Lambda>(Expression.New(typeof(T))).Compile(); + } + public TDest Map(TSource source) { - var dest = (TDest)Activator.CreateInstance(typeof(TDest))!; + var dest = ObjectFactory.Create(); return Map(source, dest); } diff --git a/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs b/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs deleted file mode 100644 index 9c7b3694d..000000000 --- a/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Linq.Expressions; - -namespace Grand.Mapping.Internal; - -internal sealed class ParameterReplacer : ExpressionVisitor -{ - private readonly ParameterExpression _oldParam; - private readonly Expression _newExpr; - - private ParameterReplacer(ParameterExpression oldParam, Expression newExpr) - { - _oldParam = oldParam; - _newExpr = newExpr; - } - - public static Expression Replace(Expression body, ParameterExpression oldParam, Expression newExpr) - => new ParameterReplacer(oldParam, newExpr).Visit(body)!; - - protected override Expression VisitParameter(ParameterExpression node) - => node == _oldParam ? _newExpr : base.VisitParameter(node); -} From e82c9f822255113faf10b433d5e7c7a21bb8da87 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Fri, 27 Mar 2026 21:17:24 +0100 Subject: [PATCH 08/16] commit --- .../Mapper/MappingExtensions.cs | 2 +- src/Core/Grand.Mapping/GrandMapper.cs | 15 ++------------- src/Core/Grand.Mapping/IMapper.cs | 2 +- .../Grand.Mapping.Tests/TestMappingHelpers.cs | 2 +- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/Core/Grand.Infrastructure/Mapper/MappingExtensions.cs b/src/Core/Grand.Infrastructure/Mapper/MappingExtensions.cs index 9f0fc981a..806e0676e 100644 --- a/src/Core/Grand.Infrastructure/Mapper/MappingExtensions.cs +++ b/src/Core/Grand.Infrastructure/Mapper/MappingExtensions.cs @@ -2,7 +2,7 @@ public static class MappingExtensions { - public static TDestination MapTo(this TSource source) + public static TDestination MapTo(this TSource source) where TDestination : new() { return AutoMapperConfig.Mapper.Map(source); } diff --git a/src/Core/Grand.Mapping/GrandMapper.cs b/src/Core/Grand.Mapping/GrandMapper.cs index 94fdccb3c..e87b13a56 100644 --- a/src/Core/Grand.Mapping/GrandMapper.cs +++ b/src/Core/Grand.Mapping/GrandMapper.cs @@ -1,5 +1,3 @@ -using System.Linq.Expressions; - namespace Grand.Mapping; internal sealed class GrandMapper : IMapper @@ -8,18 +6,9 @@ internal sealed class GrandMapper : IMapper internal GrandMapper(Dictionary<(Type, Type), Delegate> mappings) => _mappings = mappings; - // Caches a compiled parameterless constructor per TDest, avoiding Activator.CreateInstance - // reflection overhead on every Map call. - private static class ObjectFactory - { - internal static readonly Func Create = - Expression.Lambda>(Expression.New(typeof(T))).Compile(); - } - - public TDest Map(TSource source) + public TDest Map(TSource source) where TDest : new() { - var dest = ObjectFactory.Create(); - return Map(source, dest); + return Map(source, new TDest()); } public TDest Map(TSource source, TDest destination) diff --git a/src/Core/Grand.Mapping/IMapper.cs b/src/Core/Grand.Mapping/IMapper.cs index 6b3131dfe..9809f155d 100644 --- a/src/Core/Grand.Mapping/IMapper.cs +++ b/src/Core/Grand.Mapping/IMapper.cs @@ -2,6 +2,6 @@ namespace Grand.Mapping; public interface IMapper { - TDest Map(TSource source); + TDest Map(TSource source) where TDest : new(); TDest Map(TSource source, TDest destination); } diff --git a/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs b/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs index 83e6ea445..75e0b6343 100644 --- a/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs +++ b/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs @@ -14,7 +14,7 @@ public static void AddProfile(this IMapperConfigurationExpression cfg) => cfg.AddProfile(new T()); // Preserves: _mapper.Map(source) - public static TDest Map(this IMapper mapper, object source) + public static TDest Map(this IMapper mapper, object source) where TDest : new() { if (source is null) return default!; var mapMethod = typeof(IMapper) From 40ef74af92ea8bc3efdf3bb168231a179ab37c7f Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Sat, 28 Mar 2026 10:53:27 +0100 Subject: [PATCH 09/16] commit --- src/Core/Grand.Mapping/IMapperConfigurationExpression.cs | 1 - .../Internal/MapperConfigurationExpressionImpl.cs | 3 --- .../Services/ExportImport/BrandImportDataObjectTests.cs | 2 +- .../Services/ExportImport/CategoryImportDataObjectTests.cs | 2 +- .../Services/ExportImport/CollectionImportDataObjectTests.cs | 2 +- .../Services/ExportImport/ProductImportDataObjectTests.cs | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs b/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs index 3765221c3..b5c691f7c 100644 --- a/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs +++ b/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs @@ -3,5 +3,4 @@ namespace Grand.Mapping; public interface IMapperConfigurationExpression { void AddProfile(Profile profile); - void AddProfile(Type profileType); } diff --git a/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs b/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs index 64e8e6e90..f107add3f 100644 --- a/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs +++ b/src/Core/Grand.Mapping/Internal/MapperConfigurationExpressionImpl.cs @@ -7,8 +7,5 @@ internal sealed class MapperConfigurationExpressionImpl : IMapperConfigurationEx public void AddProfile(Profile profile) => _configurations.AddRange(profile.GetConfigurations()); - public void AddProfile(Type profileType) - => AddProfile((Profile)Activator.CreateInstance(profileType)!); - internal IEnumerable GetConfigurations() => _configurations; } diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs index 973840c94..ce73aed85 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/BrandImportDataObjectTests.cs @@ -180,7 +180,7 @@ private void InitAutoMapper() //create AutoMapper configuration var config = new MapperConfiguration(cfg => { - foreach (var instance in instances) cfg.AddProfile(instance.GetType()); + foreach (var instance in instances) cfg.AddProfile((Grand.Mapping.Profile)instance); }); //register automapper diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs index 1c2daa58f..e83a9ac9a 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CategoryImportDataObjectTests.cs @@ -181,7 +181,7 @@ private void InitAutoMapper() //create AutoMapper configuration var config = new MapperConfiguration(cfg => { - foreach (var instance in instances) cfg.AddProfile(instance.GetType()); + foreach (var instance in instances) cfg.AddProfile((Grand.Mapping.Profile)instance); }); //register automapper diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs index 8422129cd..17c0d396e 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/CollectionImportDataObjectTests.cs @@ -185,7 +185,7 @@ private void InitAutoMapper() //create AutoMapper configuration var config = new MapperConfiguration(cfg => { - foreach (var instance in instances) cfg.AddProfile(instance.GetType()); + foreach (var instance in instances) cfg.AddProfile((Grand.Mapping.Profile)instance); }); //register automapper diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs index 6a704b10c..469f91791 100644 --- a/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs +++ b/src/Tests/Grand.Business.Catalog.Tests/Services/ExportImport/ProductImportDataObjectTests.cs @@ -279,7 +279,7 @@ private void InitAutoMapper() //create AutoMapper configuration var config = new MapperConfiguration(cfg => { - foreach (var instance in instances) cfg.AddProfile(instance.GetType()); + foreach (var instance in instances) cfg.AddProfile((Grand.Mapping.Profile)instance); }); //register automapper From 6e2f7d737d91c7afded3b75ddb5833340de86cf2 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Sat, 28 Mar 2026 11:43:01 +0100 Subject: [PATCH 10/16] Commit --- src/Core/Grand.Mapping/GrandMapper.cs | 7 ++-- .../IMapperConfigurationExpression.cs | 1 + .../Internal/IMappingConfiguration.cs | 2 +- .../Grand.Mapping/Internal/MappingCompiler.cs | 34 ++++++++++++------- .../Internal/MappingConfiguration.cs | 4 +-- .../Internal/ParameterReplacer.cs | 22 ++++++++++++ src/Core/Grand.Mapping/MapperConfiguration.cs | 5 +-- .../Grand.Mapping.Tests/TestMappingHelpers.cs | 9 ----- 8 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 src/Core/Grand.Mapping/Internal/ParameterReplacer.cs diff --git a/src/Core/Grand.Mapping/GrandMapper.cs b/src/Core/Grand.Mapping/GrandMapper.cs index e87b13a56..a9a6a6962 100644 --- a/src/Core/Grand.Mapping/GrandMapper.cs +++ b/src/Core/Grand.Mapping/GrandMapper.cs @@ -1,10 +1,13 @@ +using System.Collections.Frozen; + namespace Grand.Mapping; internal sealed class GrandMapper : IMapper { - private readonly Dictionary<(Type, Type), Delegate> _mappings; + private readonly FrozenDictionary<(Type, Type), Delegate> _mappings; - internal GrandMapper(Dictionary<(Type, Type), Delegate> mappings) => _mappings = mappings; + internal GrandMapper(Dictionary<(Type, Type), Delegate> mappings) + => _mappings = mappings.ToFrozenDictionary(); public TDest Map(TSource source) where TDest : new() { diff --git a/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs b/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs index b5c691f7c..de4d21743 100644 --- a/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs +++ b/src/Core/Grand.Mapping/IMapperConfigurationExpression.cs @@ -3,4 +3,5 @@ namespace Grand.Mapping; public interface IMapperConfigurationExpression { void AddProfile(Profile profile); + void AddProfile() where T : Profile, new() => AddProfile(new T()); } diff --git a/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs b/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs index 67ee3c28e..5a9aab102 100644 --- a/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs +++ b/src/Core/Grand.Mapping/Internal/IMappingConfiguration.cs @@ -3,5 +3,5 @@ namespace Grand.Mapping.Internal; internal interface IMappingConfiguration { (Type Source, Type Dest) GetTypes(); - Delegate CompileDelegate(Dictionary<(Type, Type), Delegate> mappings); + Delegate CompileDelegate(HashSet<(Type, Type)> registeredTypes, Dictionary<(Type, Type), Delegate> mappings); } diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs index a8a0c18c5..64ce98170 100644 --- a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -7,8 +7,12 @@ namespace Grand.Mapping.Internal; internal static class MappingCompiler { + private static readonly MethodInfo _dictGetItemMethod = + typeof(Dictionary<(Type, Type), Delegate>).GetMethod("get_Item")!; + public static Action Compile( List configs, + HashSet<(Type, Type)> registeredTypes, Dictionary<(Type, Type), Delegate> mappings) { var src = Expression.Parameter(typeof(TSource), "src"); @@ -30,7 +34,7 @@ public static Action Compile( if (mc.IsIgnored) continue; Expression? value = mc.MapFromExpression != null - ? Expression.Invoke(mc.MapFromExpression, src) + ? InlineLambda(mc.MapFromExpression, src) : SourceProp(src, dp.Name); if (value == null) continue; @@ -40,14 +44,14 @@ public static Action Compile( { var assign = Expression.Assign(destAccess, coerced); body.Add(mc.ConditionExpression != null - ? Expression.IfThen(Expression.Invoke(mc.ConditionExpression, src), assign) + ? Expression.IfThen(InlineLambda(mc.ConditionExpression, src), assign) : (Expression)assign); } else { // Fallback: delegate cross-type mapping to a registered profile var nested = BuildNestedMapping(value, destAccess, dp.PropertyType, - mc.ConditionExpression, src, mappings); + mc.ConditionExpression, src, registeredTypes, mappings); if (nested != null) body.Add(nested); } } @@ -63,7 +67,7 @@ public static Action Compile( { // Fallback: delegate cross-type mapping to a registered profile var nested = BuildNestedMapping(srcExpr, destAccess, dp.PropertyType, - null, src, mappings); + null, src, registeredTypes, mappings); if (nested != null) body.Add(nested); } } @@ -76,12 +80,12 @@ public static Action Compile( var destAccess = SubstitutePath(pc.DestinationPathExpression!, dst); if (destAccess == null) continue; - var value = Coerce(Expression.Invoke(pc.MapFromExpression!, src), destAccess.Type); + var value = Coerce(InlineLambda(pc.MapFromExpression!, src), destAccess.Type); if (value == null) continue; var assign = Expression.Assign(destAccess, value); body.Add(pc.ConditionExpression != null - ? Expression.IfThen(Expression.Invoke(pc.ConditionExpression, src), assign) + ? Expression.IfThen(InlineLambda(pc.ConditionExpression, src), assign) : (Expression)assign); } @@ -101,6 +105,7 @@ public static Action Compile( Type destType, LambdaExpression? condition, ParameterExpression srcParam, + HashSet<(Type, Type)> registeredTypes, Dictionary<(Type, Type), Delegate> mappings) { var srcType = srcValueExpr.Type; @@ -110,7 +115,7 @@ public static Action Compile( && CollectionElementType(srcType) == null && CollectionElementType(destType) == null && destType.GetConstructor(Type.EmptyTypes) != null - && mappings.ContainsKey((srcType, destType))) + && registeredTypes.Contains((srcType, destType))) { // Capture the dictionary so the delegate can be looked up at runtime // (when all delegates are guaranteed to be compiled). @@ -118,7 +123,7 @@ public static Action Compile( var keyConst = Expression.Constant((srcType, destType)); var getDel = Expression.Call( delConst, - typeof(Dictionary<(Type, Type), Delegate>).GetMethod("get_Item")!, + _dictGetItemMethod, keyConst); var castDel = Expression.Convert( getDel, @@ -139,7 +144,7 @@ public static Action Compile( Expression.Assign(destAccess, tmpVar)))); return condition != null - ? Expression.IfThen(Expression.Invoke(condition, srcParam), innerBlock) + ? Expression.IfThen(InlineLambda(condition, srcParam), innerBlock) : (Expression)innerBlock; } @@ -147,12 +152,12 @@ public static Action Compile( var srcElem = CollectionElementType(srcType); var dstElem = CollectionElementType(destType); if (srcElem != null && dstElem != null && srcElem != dstElem - && mappings.ContainsKey((srcElem, dstElem))) + && registeredTypes.Contains((srcElem, dstElem))) { var converted = BuildCrossTypeCollectionCoerce(srcValueExpr, destType, srcElem, dstElem, mappings); Expression assignExpr = Expression.Assign(destAccess, converted); return condition != null - ? Expression.IfThen(Expression.Invoke(condition, srcParam), assignExpr) + ? Expression.IfThen(InlineLambda(condition, srcParam), assignExpr) : assignExpr; } @@ -170,7 +175,7 @@ private static Expression BuildCrossTypeCollectionCoerce( var keyConst = Expression.Constant((srcElem, dstElem)); var getDel = Expression.Call( delConst, - typeof(Dictionary<(Type, Type), Delegate>).GetMethod("get_Item")!, + _dictGetItemMethod, keyConst); var castDel = Expression.Convert(getDel, typeof(Action<,>).MakeGenericType(srcElem, dstElem)); @@ -215,6 +220,11 @@ private static Expression BuildCrossTypeCollectionCoerce( filled); } + // Inlines a single-parameter lambda body by substituting the parameter, + // avoiding an Expression.Invoke delegate-call wrapper in the compiled tree. + private static Expression InlineLambda(LambdaExpression lambda, Expression arg) + => new ParameterReplacer(lambda.Parameters[0], arg).Visit(lambda.Body); + // Returns Expression for same-named readable property on src, or null. private static Expression? SourceProp(ParameterExpression src, string name) { diff --git a/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs b/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs index f7f742203..76ee1a0d3 100644 --- a/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs +++ b/src/Core/Grand.Mapping/Internal/MappingConfiguration.cs @@ -9,6 +9,6 @@ public MappingConfiguration(MappingExpressionImpl expression) public (Type Source, Type Dest) GetTypes() => (typeof(TSource), typeof(TDest)); - public Delegate CompileDelegate(Dictionary<(Type, Type), Delegate> mappings) - => MappingCompiler.Compile(_expression.MemberConfigs, mappings); + public Delegate CompileDelegate(HashSet<(Type, Type)> registeredTypes, Dictionary<(Type, Type), Delegate> mappings) + => MappingCompiler.Compile(_expression.MemberConfigs, registeredTypes, mappings); } diff --git a/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs b/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs new file mode 100644 index 000000000..098177665 --- /dev/null +++ b/src/Core/Grand.Mapping/Internal/ParameterReplacer.cs @@ -0,0 +1,22 @@ +using System.Linq.Expressions; + +namespace Grand.Mapping.Internal; + +/// +/// Substitutes a lambda parameter with a concrete expression, enabling body inlining +/// instead of generating a delegate-call wrapper via Expression.Invoke. +/// +internal sealed class ParameterReplacer : ExpressionVisitor +{ + private readonly ParameterExpression _parameter; + private readonly Expression _replacement; + + internal ParameterReplacer(ParameterExpression parameter, Expression replacement) + { + _parameter = parameter; + _replacement = replacement; + } + + protected override Expression VisitParameter(ParameterExpression node) + => node == _parameter ? _replacement : base.VisitParameter(node); +} diff --git a/src/Core/Grand.Mapping/MapperConfiguration.cs b/src/Core/Grand.Mapping/MapperConfiguration.cs index bf79c9018..20fa3c96c 100644 --- a/src/Core/Grand.Mapping/MapperConfiguration.cs +++ b/src/Core/Grand.Mapping/MapperConfiguration.cs @@ -14,14 +14,15 @@ public MapperConfiguration(Action configure) // First pass: register all type-pair keys so nested mappings can detect // forward/cross references during compilation. + var registeredTypes = new HashSet<(Type, Type)>(configs.Count); foreach (var config in configs) - _mappings[config.GetTypes()] = null!; + registeredTypes.Add(config.GetTypes()); // Second pass: compile all delegates (all keys are now registered). foreach (var config in configs) { var key = config.GetTypes(); - _mappings[key] = config.CompileDelegate(_mappings); + _mappings[key] = config.CompileDelegate(registeredTypes, _mappings); } } diff --git a/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs b/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs index 75e0b6343..03d201a39 100644 --- a/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs +++ b/src/Tests/Grand.Mapping.Tests/TestMappingHelpers.cs @@ -2,17 +2,8 @@ namespace Grand.Mapping.Tests; -/// -/// Extension methods to preserve AutoMapper-style call syntax in tests -/// while using Grand.Mapping internally. -/// internal static class TestMappingHelpers { - // Preserves: cfg.AddProfile() - public static void AddProfile(this IMapperConfigurationExpression cfg) - where T : Profile, new() - => cfg.AddProfile(new T()); - // Preserves: _mapper.Map(source) public static TDest Map(this IMapper mapper, object source) where TDest : new() { From 1bd140e982017801bbcf151419213173fbecc7c9 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Sun, 29 Mar 2026 14:09:34 +0200 Subject: [PATCH 11/16] Update DiscountModel properties to nullable types and simplify date conversion in mapping --- .../Extensions/Mapping/DiscountMappingExtensions.cs | 4 ++-- .../Grand.Web.AdminShared/Models/Discounts/DiscountModel.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Web/Grand.Web.AdminShared/Extensions/Mapping/DiscountMappingExtensions.cs b/src/Web/Grand.Web.AdminShared/Extensions/Mapping/DiscountMappingExtensions.cs index 1a2ff8b32..2004bb700 100644 --- a/src/Web/Grand.Web.AdminShared/Extensions/Mapping/DiscountMappingExtensions.cs +++ b/src/Web/Grand.Web.AdminShared/Extensions/Mapping/DiscountMappingExtensions.cs @@ -11,8 +11,8 @@ public static class DiscountMappingExtensions public static DiscountModel ToModel(this Discount entity, IDateTimeService dateTimeService) { var discount = entity.MapTo(); - discount.StartDate = (DateTime)entity.StartDateUtc.ConvertToUserTime(dateTimeService); - discount.EndDate = (DateTime)entity.EndDateUtc.ConvertToUserTime(dateTimeService); + discount.StartDate = entity.StartDateUtc.ConvertToUserTime(dateTimeService); + discount.EndDate = entity.EndDateUtc.ConvertToUserTime(dateTimeService); return discount; } diff --git a/src/Web/Grand.Web.AdminShared/Models/Discounts/DiscountModel.cs b/src/Web/Grand.Web.AdminShared/Models/Discounts/DiscountModel.cs index d981715ae..7f17cfea1 100644 --- a/src/Web/Grand.Web.AdminShared/Models/Discounts/DiscountModel.cs +++ b/src/Web/Grand.Web.AdminShared/Models/Discounts/DiscountModel.cs @@ -47,11 +47,11 @@ public class DiscountModel : BaseEntityModel, IStoreLinkModel [GrandResourceDisplayName("admin.marketing.Discounts.Fields.StartDate")] [UIHint("DateTimeNullable")] - public DateTime StartDate { get; set; } + public DateTime? StartDate { get; set; } [GrandResourceDisplayName("admin.marketing.Discounts.Fields.EndDate")] [UIHint("DateTimeNullable")] - public DateTime EndDate { get; set; } + public DateTime? EndDate { get; set; } [GrandResourceDisplayName("admin.marketing.Discounts.Fields.RequiresCouponCode")] public bool RequiresCouponCode { get; set; } From 43fc9ef0908e9e89c2896a658c9f17268570a476 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Sun, 29 Mar 2026 14:18:16 +0200 Subject: [PATCH 12/16] Commit --- .../Grand.Mapping/Internal/MappingCompiler.cs | 8 +++-- ...del_NullableFieldsAreNull.verified.txt.bak | 11 ++++++ ...Model_NullableFields_WithNull.verified.txt | 11 ++++++ ...del_NullableFields_WithValues.verified.txt | 12 +++++++ .../AdminShared/CatalogMappingTests.cs | 34 +++++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFieldsAreNull.verified.txt.bak create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithNull.verified.txt create mode 100644 src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithValues.verified.txt diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs index 64ce98170..f643f5332 100644 --- a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -261,10 +261,14 @@ private static Expression InlineLambda(LambdaExpression lambda, Expression arg) if (underlyingTarget == expr.Type) return Expression.Convert(expr, target); - // Nullable → T + // Nullable → T: use HasValue check to avoid InvalidOperationException on null values. + // Maps to default(T) when the source nullable is null (same behaviour as AutoMapper). var underlyingSource = Nullable.GetUnderlyingType(expr.Type); if (underlyingSource == target) - return Expression.Convert(expr, target); + return Expression.Condition( + Expression.Property(expr, nameof(Nullable.HasValue)), + Expression.Property(expr, nameof(Nullable.Value)), + Expression.Default(target)); // Collection coercions with null guard (AutoMapper AllowNullCollections=false behaviour). // Runs before IsAssignableFrom to avoid IList/T[] cross-type issues. diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFieldsAreNull.verified.txt.bak b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFieldsAreNull.verified.txt.bak new file mode 100644 index 000000000..cfe089dd0 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFieldsAreNull.verified.txt.bak @@ -0,0 +1,11 @@ +{ + Name: No Dates Discount, + DiscountTypeId: 1, + UsePercentage: false, + CalculateByPlugin: false, + RequiresCouponCode: false, + Reused: false, + IsCumulative: false, + IsEnabled: true, + Id: disc-002 +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithNull.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithNull.verified.txt new file mode 100644 index 000000000..f3d3f8d29 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithNull.verified.txt @@ -0,0 +1,11 @@ +{ + Name: No Dates Discount, + DiscountTypeId: 1, + UsePercentage: false, + CalculateByPlugin: false, + RequiresCouponCode: false, + Reused: false, + IsCumulative: false, + IsEnabled: true, + Id: disc-003 +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithValues.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithValues.verified.txt new file mode 100644 index 000000000..fc9aae9e7 --- /dev/null +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.Discount_ToModel_NullableFields_WithValues.verified.txt @@ -0,0 +1,12 @@ +{ + Name: Summer Sale, + DiscountTypeId: 1, + UsePercentage: false, + CalculateByPlugin: false, + MaximumDiscountAmount: 50.0, + RequiresCouponCode: false, + Reused: false, + IsCumulative: false, + IsEnabled: true, + Id: disc-002 +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs index 500b93cd6..f5c79642c 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CatalogMappingTests.cs @@ -266,6 +266,40 @@ public Task Discount_ToModel() return Verify(result); } + [TestMethod] + public Task Discount_ToModel_NullableFields_WithValues() + { + var source = new Discount { + Id = "disc-002", + Name = "Summer Sale", + DiscountTypeId = (DiscountType)1, + MaximumDiscountAmount = 50.0, + StartDateUtc = new DateTime(2024, 6, 1, 0, 0, 0, DateTimeKind.Utc), + EndDateUtc = new DateTime(2024, 8, 31, 23, 59, 59, DateTimeKind.Utc), + IsEnabled = true + }; + var result = _mapper.Map(source); + return Verify(result); + } + + [TestMethod] + public Task Discount_ToModel_NullableFields_WithNull() + { + // Regression: Nullable → T coercion threw InvalidOperationException + // when the source nullable was null (e.g. MaximumDiscountAmount, StartDateUtc). + var source = new Discount { + Id = "disc-003", + Name = "No Dates Discount", + DiscountTypeId = (DiscountType)1, + // MaximumDiscountAmount = null (double?) + // StartDateUtc = null (DateTime?) + // EndDateUtc = null (DateTime?) + IsEnabled = true + }; + var result = _mapper.Map(source); + return Verify(result); + } + [TestMethod] public Task DiscountModel_ToDomain() { From 7ec346efbe9895fe507519e816b552d8351bbc9a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:12:50 +0000 Subject: [PATCH 13/16] Revert GrandNode.sln Visual Studio version header to VS 17 Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/67f1dafa-8346-4ebb-ac3b-debc835fd20c Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com> --- GrandNode.sln | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GrandNode.sln b/GrandNode.sln index 751a09a8a..20ae11d7c 100644 --- a/GrandNode.sln +++ b/GrandNode.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.5.11619.145 insiders +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{FA350BD6-C29D-40D9-BA47-FE5FBDFC84A0}" EndProject From 52159fc0f14034aba6ecc0895352d8f1f34ac740 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:34:50 +0000 Subject: [PATCH 14/16] Add where TDest : new() constraint to Profile.CreateMap to align with IMapper.Map Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/ae3284e7-7afc-43c0-941a-1147ab714de9 Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com> --- src/Core/Grand.Mapping/Profile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Grand.Mapping/Profile.cs b/src/Core/Grand.Mapping/Profile.cs index 775e816c0..0760becdc 100644 --- a/src/Core/Grand.Mapping/Profile.cs +++ b/src/Core/Grand.Mapping/Profile.cs @@ -6,7 +6,7 @@ public abstract class Profile { private readonly List _configurations = new(); - protected IMappingExpression CreateMap() + protected IMappingExpression CreateMap() where TDest : new() { var expr = new MappingExpressionImpl(); _configurations.Add(new MappingConfiguration(expr)); From 5e2cde0599fa9f7a6ef3f1006896a461c4192538 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:49:06 +0000 Subject: [PATCH 15/16] Throw ArgumentNullException for null source/destination in GrandMapper.Map Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/072caf8c-f584-41c6-b486-2828cf669365 Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com> --- src/Core/Grand.Mapping/GrandMapper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Grand.Mapping/GrandMapper.cs b/src/Core/Grand.Mapping/GrandMapper.cs index a9a6a6962..08771d597 100644 --- a/src/Core/Grand.Mapping/GrandMapper.cs +++ b/src/Core/Grand.Mapping/GrandMapper.cs @@ -16,7 +16,8 @@ internal GrandMapper(Dictionary<(Type, Type), Delegate> mappings) public TDest Map(TSource source, TDest destination) { - if (source is null || destination is null) return destination!; + ArgumentNullException.ThrowIfNull(source); + ArgumentNullException.ThrowIfNull(destination); if (_mappings.TryGetValue((typeof(TSource), typeof(TDest)), out var del)) ((Action)del)(source, destination); return destination; From 3a4e8c6e71784ab22b449565b19f102732594fe7 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Sun, 29 Mar 2026 17:55:41 +0200 Subject: [PATCH 16/16] Refactor mapping compiler for modularity and clarity --- .../Grand.Mapping/Internal/MappingCompiler.cs | 280 ++++++++++-------- 1 file changed, 153 insertions(+), 127 deletions(-) diff --git a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs index f643f5332..3cb4bde0c 100644 --- a/src/Core/Grand.Mapping/Internal/MappingCompiler.cs +++ b/src/Core/Grand.Mapping/Internal/MappingCompiler.cs @@ -19,86 +19,110 @@ public static Action Compile( var dst = Expression.Parameter(typeof(TDest), "dst"); var body = new List(); - var direct = new Dictionary(StringComparer.Ordinal); - foreach (var c in configs.Where(c => !c.IsPath)) - direct[c.MemberName] = c; + var direct = BuildDirectConfigLookup(configs); foreach (var dp in typeof(TDest) .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanWrite)) { var destAccess = Expression.Property(dst, dp); - if (direct.TryGetValue(dp.Name, out var mc)) - { - if (mc.IsIgnored) continue; - - Expression? value = mc.MapFromExpression != null - ? InlineLambda(mc.MapFromExpression, src) - : SourceProp(src, dp.Name); - - if (value == null) continue; - - var coerced = Coerce(value, dp.PropertyType); - if (coerced != null) - { - var assign = Expression.Assign(destAccess, coerced); - body.Add(mc.ConditionExpression != null - ? Expression.IfThen(InlineLambda(mc.ConditionExpression, src), assign) - : (Expression)assign); - } - else - { - // Fallback: delegate cross-type mapping to a registered profile - var nested = BuildNestedMapping(value, destAccess, dp.PropertyType, - mc.ConditionExpression, src, registeredTypes, mappings); - if (nested != null) body.Add(nested); - } - } + ProcessMappedProperty(body, dp, mc, src, destAccess, registeredTypes, mappings); else - { - var srcExpr = SourceProp(src, dp.Name); - if (srcExpr == null) continue; - - var value = Coerce(srcExpr, dp.PropertyType); - if (value != null) - body.Add(Expression.Assign(destAccess, value)); - else - { - // Fallback: delegate cross-type mapping to a registered profile - var nested = BuildNestedMapping(srcExpr, destAccess, dp.PropertyType, - null, src, registeredTypes, mappings); - if (nested != null) body.Add(nested); - } - } + ProcessAutoProperty(body, dp, src, destAccess, registeredTypes, mappings); } - // ForPath configs + ProcessForPaths(body, configs, src, dst); + + return Expression.Lambda>( + body.Count > 0 ? (Expression)Expression.Block(body) : Expression.Empty(), + src, dst).Compile(); + } + + private static Dictionary BuildDirectConfigLookup(List configs) + { + var direct = new Dictionary(StringComparer.Ordinal); + foreach (var c in configs.Where(c => !c.IsPath)) + direct[c.MemberName] = c; + return direct; + } + + private static void ProcessMappedProperty( + List body, PropertyInfo dp, MemberConfig mc, + ParameterExpression src, Expression destAccess, + HashSet<(Type, Type)> registeredTypes, + Dictionary<(Type, Type), Delegate> mappings) + { + if (mc.IsIgnored) return; + + var value = mc.MapFromExpression != null + ? InlineLambda(mc.MapFromExpression, src) + : SourceProp(src, dp.Name); + + if (value == null) return; + + var expr = BuildAssignOrNested(value, destAccess, dp.PropertyType, + mc.ConditionExpression, src, registeredTypes, mappings); + if (expr != null) body.Add(expr); + } + + private static void ProcessAutoProperty( + List body, PropertyInfo dp, + ParameterExpression src, Expression destAccess, + HashSet<(Type, Type)> registeredTypes, + Dictionary<(Type, Type), Delegate> mappings) + { + var srcExpr = SourceProp(src, dp.Name); + if (srcExpr == null) return; + + var expr = BuildAssignOrNested(srcExpr, destAccess, dp.PropertyType, + null, src, registeredTypes, mappings); + if (expr != null) body.Add(expr); + } + + private static Expression? BuildAssignOrNested( + Expression value, Expression destAccess, Type destType, + LambdaExpression? condition, ParameterExpression src, + HashSet<(Type, Type)> registeredTypes, + Dictionary<(Type, Type), Delegate> mappings) + { + var coerced = Coerce(value, destType); + if (coerced != null) + return WrapWithCondition(Expression.Assign(destAccess, coerced), condition, src); + + return BuildNestedMapping(value, destAccess, destType, condition, src, registeredTypes, mappings); + } + + private static void ProcessForPaths( + List body, List configs, + ParameterExpression src, ParameterExpression dst) + { foreach (var pc in configs.Where(c => c.IsPath && !c.IsIgnored && c.MapFromExpression != null && c.DestinationPathExpression != null)) { - var destAccess = SubstitutePath(pc.DestinationPathExpression!, dst); - if (destAccess == null) continue; + var expr = BuildForPathExpression(pc, src, dst); + if (expr != null) body.Add(expr); + } + } - var value = Coerce(InlineLambda(pc.MapFromExpression!, src), destAccess.Type); - if (value == null) continue; + private static Expression? BuildForPathExpression( + MemberConfig pc, ParameterExpression src, ParameterExpression dst) + { + var destAccess = SubstitutePath(pc.DestinationPathExpression!, dst); + if (destAccess == null) return null; - var assign = Expression.Assign(destAccess, value); - body.Add(pc.ConditionExpression != null - ? Expression.IfThen(InlineLambda(pc.ConditionExpression, src), assign) - : (Expression)assign); - } + var value = Coerce(InlineLambda(pc.MapFromExpression!, src), destAccess.Type); + if (value == null) return null; - return Expression.Lambda>( - body.Count > 0 ? (Expression)Expression.Block(body) : Expression.Empty(), - src, dst).Compile(); + return WrapWithCondition(Expression.Assign(destAccess, value), pc.ConditionExpression, src); } - /// - /// Generates an expression that maps srcValueExpr → destAccess using a registered - /// profile mapping. Handles both single objects (A → B) and collections (IList<A> → IList<B>). - /// Returns null when no applicable registered mapping exists. - /// + private static Expression WrapWithCondition( + Expression expr, LambdaExpression? condition, ParameterExpression src) + => condition != null + ? Expression.IfThen(InlineLambda(condition, src), expr) + : expr; + private static Expression? BuildNestedMapping( Expression srcValueExpr, Expression destAccess, @@ -110,60 +134,57 @@ public static Action Compile( { var srcType = srcValueExpr.Type; - // Case 1: A → B (both non-value, non-collection reference types with registered mapping) - if (!srcType.IsValueType && !destType.IsValueType - && CollectionElementType(srcType) == null - && CollectionElementType(destType) == null - && destType.GetConstructor(Type.EmptyTypes) != null - && registeredTypes.Contains((srcType, destType))) + if (IsDirectObjectMapping(srcType, destType, registeredTypes)) { - // Capture the dictionary so the delegate can be looked up at runtime - // (when all delegates are guaranteed to be compiled). - var delConst = Expression.Constant(mappings); - var keyConst = Expression.Constant((srcType, destType)); - var getDel = Expression.Call( - delConst, - _dictGetItemMethod, - keyConst); - var castDel = Expression.Convert( - getDel, - typeof(Action<,>).MakeGenericType(srcType, destType)); - - // Cache source value to avoid double-evaluation (e.g. when srcValueExpr is Invoke) - var srcVar = Expression.Variable(srcType, "ns"); - var tmpVar = Expression.Variable(destType, "nd"); - - var innerBlock = Expression.Block( - new[] { srcVar, tmpVar }, - Expression.Assign(srcVar, srcValueExpr), - Expression.IfThen( - Expression.ReferenceNotEqual(srcVar, Expression.Constant(null, srcType)), - Expression.Block( - Expression.Assign(tmpVar, Expression.New(destType)), - Expression.Invoke(castDel, srcVar, tmpVar), - Expression.Assign(destAccess, tmpVar)))); - - return condition != null - ? Expression.IfThen(InlineLambda(condition, srcParam), innerBlock) - : (Expression)innerBlock; + var innerBlock = BuildDirectObjectMappingBlock( + srcValueExpr, destAccess, srcType, destType, mappings); + return WrapWithCondition(innerBlock, condition, srcParam); } - // Case 2: IList → IList / A[] → B[] where A→B mapping is registered var srcElem = CollectionElementType(srcType); var dstElem = CollectionElementType(destType); if (srcElem != null && dstElem != null && srcElem != dstElem && registeredTypes.Contains((srcElem, dstElem))) { var converted = BuildCrossTypeCollectionCoerce(srcValueExpr, destType, srcElem, dstElem, mappings); - Expression assignExpr = Expression.Assign(destAccess, converted); - return condition != null - ? Expression.IfThen(InlineLambda(condition, srcParam), assignExpr) - : assignExpr; + return WrapWithCondition(Expression.Assign(destAccess, converted), condition, srcParam); } return null; } + private static bool IsDirectObjectMapping( + Type srcType, Type destType, HashSet<(Type, Type)> registeredTypes) + => !srcType.IsValueType && !destType.IsValueType + && CollectionElementType(srcType) == null + && CollectionElementType(destType) == null + && destType.GetConstructor(Type.EmptyTypes) != null + && registeredTypes.Contains((srcType, destType)); + + private static Expression BuildDirectObjectMappingBlock( + Expression srcValueExpr, Expression destAccess, + Type srcType, Type destType, + Dictionary<(Type, Type), Delegate> mappings) + { + var castDel = Expression.Convert( + Expression.Call(Expression.Constant(mappings), _dictGetItemMethod, + Expression.Constant((srcType, destType))), + typeof(Action<,>).MakeGenericType(srcType, destType)); + + var srcVar = Expression.Variable(srcType, "ns"); + var tmpVar = Expression.Variable(destType, "nd"); + + return Expression.Block( + new[] { srcVar, tmpVar }, + Expression.Assign(srcVar, srcValueExpr), + Expression.IfThen( + Expression.ReferenceNotEqual(srcVar, Expression.Constant(null, srcType)), + Expression.Block( + Expression.Assign(tmpVar, Expression.New(destType)), + Expression.Invoke(castDel, srcVar, tmpVar), + Expression.Assign(destAccess, tmpVar)))); + } + private static Expression BuildCrossTypeCollectionCoerce( Expression src, Type destType, @@ -179,7 +200,6 @@ private static Expression BuildCrossTypeCollectionCoerce( keyConst); var castDel = Expression.Convert(getDel, typeof(Action<,>).MakeGenericType(srcElem, dstElem)); - // x => { var tmp = new DstElem(); del(x, tmp); return tmp; } var xParam = Expression.Parameter(srcElem, "x"); var tmpVar = Expression.Variable(dstElem, "tmp"); var selectorBody = Expression.Block( @@ -220,19 +240,15 @@ private static Expression BuildCrossTypeCollectionCoerce( filled); } - // Inlines a single-parameter lambda body by substituting the parameter, - // avoiding an Expression.Invoke delegate-call wrapper in the compiled tree. private static Expression InlineLambda(LambdaExpression lambda, Expression arg) => new ParameterReplacer(lambda.Parameters[0], arg).Visit(lambda.Body); - // Returns Expression for same-named readable property on src, or null. private static Expression? SourceProp(ParameterExpression src, string name) { var sp = src.Type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); return sp?.CanRead == true ? Expression.Property(src, sp) : null; } - // Rebuilds (TDest d) => d.Outer.Inner with our dst parameter — no ExpressionVisitor needed. private static MemberExpression? SubstitutePath(LambdaExpression pathExpr, ParameterExpression dst) { var chain = new List(); @@ -250,7 +266,6 @@ private static Expression InlineLambda(LambdaExpression lambda, Expression arg) return result as MemberExpression; } - // Build-time type coercion inside Expression Tree. Returns null → skip property. private static Expression? Coerce(Expression? expr, Type target) { if (expr == null) return null; @@ -261,35 +276,46 @@ private static Expression InlineLambda(LambdaExpression lambda, Expression arg) if (underlyingTarget == expr.Type) return Expression.Convert(expr, target); - // Nullable → T: use HasValue check to avoid InvalidOperationException on null values. - // Maps to default(T) when the source nullable is null (same behaviour as AutoMapper). + return CoerceNullableToValue(expr, target) + ?? CoerceCollections(expr, target) + ?? CoerceUpcast(expr, target) + ?? CoerceNumericOrEnum(expr, target) + ?? CoerceViaConvertOperator(expr, target); + } + + private static Expression? CoerceNullableToValue(Expression expr, Type target) + { var underlyingSource = Nullable.GetUnderlyingType(expr.Type); - if (underlyingSource == target) - return Expression.Condition( - Expression.Property(expr, nameof(Nullable.HasValue)), - Expression.Property(expr, nameof(Nullable.Value)), - Expression.Default(target)); - - // Collection coercions with null guard (AutoMapper AllowNullCollections=false behaviour). - // Runs before IsAssignableFrom to avoid IList/T[] cross-type issues. + if (underlyingSource != target) return null; + return Expression.Condition( + Expression.Property(expr, nameof(Nullable.HasValue)), + Expression.Property(expr, nameof(Nullable.Value)), + Expression.Default(target)); + } + + private static Expression? CoerceCollections(Expression expr, Type target) + { var srcElem = CollectionElementType(expr.Type); var dstElem = CollectionElementType(target); - if (srcElem != null && dstElem != null && srcElem == dstElem) - return BuildCollectionCoerce(expr, target, dstElem); + if (srcElem == null || dstElem == null || srcElem != dstElem) return null; + return BuildCollectionCoerce(expr, target, dstElem); + } - // Direct upcast (no Convert node needed for reference types / value subtypes) - if (target.IsAssignableFrom(expr.Type)) return expr; + private static Expression? CoerceUpcast(Expression expr, Type target) + => target.IsAssignableFrom(expr.Type) ? expr : null; - // Numeric / enum value-type conversion - if (IsNumericOrEnum(expr.Type) && IsNumericOrEnum(target)) - try { return Expression.Convert(expr, target); } catch { return null; } + private static Expression? CoerceNumericOrEnum(Expression expr, Type target) + { + if (!IsNumericOrEnum(expr.Type) || !IsNumericOrEnum(target)) return null; + try { return Expression.Convert(expr, target); } catch { return null; } + } - // User-defined conversion operator only (Method != null prevents reference downcast) + private static Expression? CoerceViaConvertOperator(Expression expr, Type target) + { try { var c = Expression.Convert(expr, target); return c.Method != null ? c : null; } catch { return null; } } - // null source → empty collection; non-null source → ToArray/ToList copy. private static Expression BuildCollectionCoerce(Expression src, Type target, Type elem) { var iEnum = typeof(IEnumerable<>).MakeGenericType(elem);