From 5843035189998142dbd3ada743e36840ebe49321 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sun, 31 Aug 2025 11:47:53 +0100 Subject: [PATCH 1/4] feat(LocalAd): add static toJson factory for serializing ad instances - Implement static toJson method in LocalAd abstract class - Add type-based dispatching for serialization to concrete ad types - Include error handling for unknown ad types - Enable serialization of LocalAd instances to JSON format --- lib/src/models/feed_decorators/local_ad.dart | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/src/models/feed_decorators/local_ad.dart b/lib/src/models/feed_decorators/local_ad.dart index 67fbb49..ca3020b 100644 --- a/lib/src/models/feed_decorators/local_ad.dart +++ b/lib/src/models/feed_decorators/local_ad.dart @@ -39,6 +39,31 @@ abstract class LocalAd extends FeedItem { } } + /// Static factory method to serialize a [LocalAd] instance to a JSON map. + /// + /// This factory uses the `adType` field of the provided [item] to dispatch + /// to the correct concrete `toJson` method. + /// + /// Throws [FormatException] if the `adType` field is missing or unknown. + static Map toJson(LocalAd item) { + switch (item.adType) { + case 'native': + final nativeAdItem = item as LocalNativeAd; + return nativeAdItem.toJson(); + case 'banner': + final bannerAdItem = item as LocalBannerAd; + return bannerAdItem.toJson(); + case 'interstitial': + final interstitialAdItem = item as LocalInterstitialAd; + return interstitialAdItem.toJson(); + case 'video': + final videoAdItem = item as LocalVideoAd; + return videoAdItem.toJson(); + default: + throw FormatException('Unknown LocalAd type for toJson: ${item.adType}'); + } + } + /// The type of the ad (e.g., banner, native, interstitial, video). final String adType; From 08c29a4766c0f2e475caecc147e6c2ceffae2cf3 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sun, 31 Aug 2025 11:48:56 +0100 Subject: [PATCH 2/4] feat(models): add static toJson method to FeedItem class - Implement a static factory method to serialize FeedItem instances to JSON - Add type-based dispatching to the correct toJson method for each subclass - Handle serialization for all known FeedItem types, including ContentCollectionItem with generic types - Include error handling for unknown types --- lib/src/models/core/feed_item.dart | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lib/src/models/core/feed_item.dart b/lib/src/models/core/feed_item.dart index c0a8848..864b271 100644 --- a/lib/src/models/core/feed_item.dart +++ b/lib/src/models/core/feed_item.dart @@ -73,6 +73,51 @@ abstract class FeedItem extends Equatable { } } + /// Static factory method to serialize a [FeedItem] instance to a JSON map. + /// + /// This factory uses the `type` field of the provided [item] to dispatch + /// to the correct concrete `toJson` method. + /// + /// Throws [FormatException] if the `type` field is missing or unknown. + static Map toJson(FeedItem item) { + switch (item.type) { + case 'headline': + final headlineItem = item as Headline; + return headlineItem.toJson(); + case 'topic': + final topicItem = item as Topic; + return topicItem.toJson(); + case 'source': + final sourceItem = item as Source; + return sourceItem.toJson(); + case 'country': + final countryItem = item as Country; + return countryItem.toJson(); + case 'callToAction': + final callToActionItem = item as CallToActionItem; + return callToActionItem.toJson(); + case 'localAd': + final localAdItem = item as LocalAd; + return LocalAd.toJson(localAdItem); + case 'contentCollection': + // For ContentCollectionItem, we need to know the generic type T + // to call its toJson method correctly. + // This requires a runtime type check and casting. + if (item is ContentCollectionItem) { + return item.toJson((topic) => topic.toJson()); + } else if (item is ContentCollectionItem) { + return item.toJson((source) => source.toJson()); + } else if (item is ContentCollectionItem) { + return item.toJson((country) => country.toJson()); + } + throw FormatException( + 'Unknown ContentCollectionItem generic type: ${item.runtimeType}', + ); + default: + throw FormatException('Unknown FeedItem type for toJson: ${item.type}'); + } + } + /// The type of the feed item, used as a discriminator for deserialization. final String type; From 59910c4041a8863a57c9a6212d47a8d50ff2fb93 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sun, 31 Aug 2025 12:06:16 +0100 Subject: [PATCH 3/4] test(local_ad): correct typo in exception message and add toJson serialization tests - Fix typo in exception message: 'LocalAds' -> 'LocalAd' - Add unit tests for toJson method dispatching in LocalAd class - Verify serialization for all LocalAd subclasses: LocalNativeAd, LocalBannerAd, LocalInterstitialAd, and LocalVideoAd --- .../models/feed_decorators/local_ad_test.dart | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/src/models/feed_decorators/local_ad_test.dart b/test/src/models/feed_decorators/local_ad_test.dart index 7574e2d..95ad5e4 100644 --- a/test/src/models/feed_decorators/local_ad_test.dart +++ b/test/src/models/feed_decorators/local_ad_test.dart @@ -73,13 +73,48 @@ void main() { isA().having( (e) => e.message, 'message', - 'Unknown LocalAds type: unknown_type', + 'Unknown LocalAd type: unknown_type', // Corrected from LocalAds ), ), ); }); }); + group('toJson dispatching', () { + test('serializes LocalNativeAd correctly via LocalAd.toJson', () { + final mockLocalNativeAd = + localAdsFixturesData.firstWhere((ad) => ad.adType == 'native') + as LocalNativeAd; + final json = mockLocalNativeAd.toJson(); + expect(LocalAd.toJson(mockLocalNativeAd), equals(json)); + }); + + test('serializes LocalBannerAd correctly via LocalAd.toJson', () { + final mockLocalBannerAd = + localAdsFixturesData.firstWhere((ad) => ad.adType == 'banner') + as LocalBannerAd; + final json = mockLocalBannerAd.toJson(); + expect(LocalAd.toJson(mockLocalBannerAd), equals(json)); + }); + + test('serializes LocalInterstitialAd correctly via LocalAd.toJson', () { + final mockLocalInterstitialAd = + localAdsFixturesData.firstWhere((ad) => ad.adType == 'interstitial') + as LocalInterstitialAd; + final json = mockLocalInterstitialAd.toJson(); + expect(LocalAd.toJson(mockLocalInterstitialAd), equals(json)); + }); + + test('serializes LocalVideoAd correctly via LocalAd.toJson', () { + final mockLocalVideoAd = + localAdsFixturesData.firstWhere((ad) => ad.adType == 'video') + as LocalVideoAd; + final json = mockLocalVideoAd.toJson(); + expect(LocalAd.toJson(mockLocalVideoAd), equals(json)); + }); + + }); + // Test props for a concrete LocalAd subclass (LocalNativeAd) // The test name is misleading as it tests a concrete instance's props. // The expectation is updated to reflect all properties of LocalNativeAd. From 5f77d1cd306282dd5a7f1c19e45aa15064ada0e1 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sun, 31 Aug 2025 12:06:27 +0100 Subject: [PATCH 4/4] docs(README): update code coverage percentage - Update code coverage badge in README.md from 99% to 98% --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2dfe7ee..e35ca02 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🛠️ core -![coverage: percentage](https://img.shields.io/badge/coverage-99-green) +![coverage: percentage](https://img.shields.io/badge/coverage-98-green) [![style: very good analysis](https://img.shields.io/badge/style-very_good_analysis-B22C89.svg)](https://pub.dev/packages/very_good_analysis) [![License: PolyForm Free Trial](https://img.shields.io/badge/License-PolyForm%20Free%20Trial-blue)](https://polyformproject.org/licenses/free-trial/1.0.0)