Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
45 changes: 45 additions & 0 deletions lib/src/models/core/feed_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, dynamic> 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<Topic>) {
return item.toJson((topic) => topic.toJson());
} else if (item is ContentCollectionItem<Source>) {
return item.toJson((source) => source.toJson());
} else if (item is ContentCollectionItem<Country>) {
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;

Expand Down
25 changes: 25 additions & 0 deletions lib/src/models/feed_decorators/local_ad.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, dynamic> 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;

Expand Down
37 changes: 36 additions & 1 deletion test/src/models/feed_decorators/local_ad_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,48 @@ void main() {
isA<FormatException>().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.
Expand Down
Loading