diff --git a/.coveragerc b/.coveragerc index ceff3384202..f6368de7d89 100644 --- a/.coveragerc +++ b/.coveragerc @@ -361,6 +361,8 @@ omit = homeassistant/components/environment_canada/weather.py homeassistant/components/envisalink/* homeassistant/components/ephember/climate.py + homeassistant/components/epic_games_store/__init__.py + homeassistant/components/epic_games_store/coordinator.py homeassistant/components/epion/__init__.py homeassistant/components/epion/coordinator.py homeassistant/components/epion/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index ef997cfa896..5dcf4b3df81 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -398,6 +398,8 @@ build.json @home-assistant/supervisor /homeassistant/components/environment_canada/ @gwww @michaeldavie /tests/components/environment_canada/ @gwww @michaeldavie /homeassistant/components/ephember/ @ttroy50 +/homeassistant/components/epic_games_store/ @hacf-fr @Quentame +/tests/components/epic_games_store/ @hacf-fr @Quentame /homeassistant/components/epion/ @lhgravendeel /tests/components/epion/ @lhgravendeel /homeassistant/components/epson/ @pszafer diff --git a/homeassistant/components/epic_games_store/__init__.py b/homeassistant/components/epic_games_store/__init__.py new file mode 100644 index 00000000000..af25eb98137 --- /dev/null +++ b/homeassistant/components/epic_games_store/__init__.py @@ -0,0 +1,35 @@ +"""The Epic Games Store integration.""" + +from __future__ import annotations + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .coordinator import EGSCalendarUpdateCoordinator + +PLATFORMS: list[Platform] = [ + Platform.CALENDAR, +] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Epic Games Store from a config entry.""" + + coordinator = EGSCalendarUpdateCoordinator(hass, entry) + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/epic_games_store/calendar.py b/homeassistant/components/epic_games_store/calendar.py new file mode 100644 index 00000000000..75c448e8467 --- /dev/null +++ b/homeassistant/components/epic_games_store/calendar.py @@ -0,0 +1,97 @@ +"""Calendar platform for a Epic Games Store.""" + +from __future__ import annotations + +from collections import namedtuple +from datetime import datetime +from typing import Any + +from homeassistant.components.calendar import CalendarEntity, CalendarEvent +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, CalendarType +from .coordinator import EGSCalendarUpdateCoordinator + +DateRange = namedtuple("DateRange", ["start", "end"]) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the local calendar platform.""" + coordinator: EGSCalendarUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + entities = [ + EGSCalendar(coordinator, entry.entry_id, CalendarType.FREE), + EGSCalendar(coordinator, entry.entry_id, CalendarType.DISCOUNT), + ] + async_add_entities(entities) + + +class EGSCalendar(CoordinatorEntity[EGSCalendarUpdateCoordinator], CalendarEntity): + """A calendar entity by Epic Games Store.""" + + _attr_has_entity_name = True + + def __init__( + self, + coordinator: EGSCalendarUpdateCoordinator, + config_entry_id: str, + cal_type: CalendarType, + ) -> None: + """Initialize EGSCalendar.""" + super().__init__(coordinator) + self._cal_type = cal_type + self._attr_translation_key = f"{cal_type}_games" + self._attr_unique_id = f"{config_entry_id}-{cal_type}" + self._attr_device_info = DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, config_entry_id)}, + manufacturer="Epic Games Store", + name="Epic Games Store", + ) + + @property + def event(self) -> CalendarEvent | None: + """Return the next upcoming event.""" + if event := self.coordinator.data[self._cal_type]: + return _get_calendar_event(event[0]) + return None + + async def async_get_events( + self, hass: HomeAssistant, start_date: datetime, end_date: datetime + ) -> list[CalendarEvent]: + """Get all events in a specific time frame.""" + events = filter( + lambda game: _are_date_range_overlapping( + DateRange(start=game["discount_start_at"], end=game["discount_end_at"]), + DateRange(start=start_date, end=end_date), + ), + self.coordinator.data[self._cal_type], + ) + return [_get_calendar_event(event) for event in events] + + +def _get_calendar_event(event: dict[str, Any]) -> CalendarEvent: + """Return a CalendarEvent from an API event.""" + return CalendarEvent( + summary=event["title"], + start=event["discount_start_at"], + end=event["discount_end_at"], + description=f"{event['description']}\n\n{event['url']}", + ) + + +def _are_date_range_overlapping(range1: DateRange, range2: DateRange) -> bool: + """Return a CalendarEvent from an API event.""" + latest_start = max(range1.start, range2.start) + earliest_end = min(range1.end, range2.end) + delta = (earliest_end - latest_start).days + 1 + overlap = max(0, delta) + return overlap > 0 diff --git a/homeassistant/components/epic_games_store/config_flow.py b/homeassistant/components/epic_games_store/config_flow.py new file mode 100644 index 00000000000..2ae86060ba2 --- /dev/null +++ b/homeassistant/components/epic_games_store/config_flow.py @@ -0,0 +1,96 @@ +"""Config flow for Epic Games Store integration.""" + +from __future__ import annotations + +import logging +from typing import Any + +from epicstore_api import EpicGamesStoreAPI +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.config_entries import ConfigFlowResult +from homeassistant.const import CONF_COUNTRY, CONF_LANGUAGE +from homeassistant.core import HomeAssistant +from homeassistant.helpers.selector import ( + CountrySelector, + LanguageSelector, + LanguageSelectorConfig, +) + +from .const import DOMAIN, SUPPORTED_LANGUAGES + +_LOGGER = logging.getLogger(__name__) + +STEP_USER_DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_LANGUAGE): LanguageSelector( + LanguageSelectorConfig(languages=SUPPORTED_LANGUAGES) + ), + vol.Required(CONF_COUNTRY): CountrySelector(), + } +) + + +def get_default_language(hass: HomeAssistant) -> str | None: + """Get default language code based on Home Assistant config.""" + language_code = f"{hass.config.language}-{hass.config.country}" + if language_code in SUPPORTED_LANGUAGES: + return language_code + if hass.config.language in SUPPORTED_LANGUAGES: + return hass.config.language + return None + + +async def validate_input(hass: HomeAssistant, user_input: dict[str, Any]) -> None: + """Validate the user input allows us to connect.""" + api = EpicGamesStoreAPI(user_input[CONF_LANGUAGE], user_input[CONF_COUNTRY]) + data = await hass.async_add_executor_job(api.get_free_games) + + if data.get("errors"): + _LOGGER.warning(data["errors"]) + + assert data["data"]["Catalog"]["searchStore"]["elements"] + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Epic Games Store.""" + + VERSION = 1 + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle the initial step.""" + data_schema = self.add_suggested_values_to_schema( + STEP_USER_DATA_SCHEMA, + user_input + or { + CONF_LANGUAGE: get_default_language(self.hass), + CONF_COUNTRY: self.hass.config.country, + }, + ) + if user_input is None: + return self.async_show_form(step_id="user", data_schema=data_schema) + + await self.async_set_unique_id( + f"freegames-{user_input[CONF_LANGUAGE]}-{user_input[CONF_COUNTRY]}" + ) + self._abort_if_unique_id_configured() + + errors = {} + + try: + await validate_input(self.hass, user_input) + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + return self.async_create_entry( + title=f"Epic Games Store - Free Games ({user_input[CONF_LANGUAGE]}-{user_input[CONF_COUNTRY]})", + data=user_input, + ) + + return self.async_show_form( + step_id="user", data_schema=data_schema, errors=errors + ) diff --git a/homeassistant/components/epic_games_store/const.py b/homeassistant/components/epic_games_store/const.py new file mode 100644 index 00000000000..c397698fd0c --- /dev/null +++ b/homeassistant/components/epic_games_store/const.py @@ -0,0 +1,31 @@ +"""Constants for the Epic Games Store integration.""" + +from enum import StrEnum + +DOMAIN = "epic_games_store" + +SUPPORTED_LANGUAGES = [ + "ar", + "de", + "en-US", + "es-ES", + "es-MX", + "fr", + "it", + "ja", + "ko", + "pl", + "pt-BR", + "ru", + "th", + "tr", + "zh-CN", + "zh-Hant", +] + + +class CalendarType(StrEnum): + """Calendar types.""" + + FREE = "free" + DISCOUNT = "discount" diff --git a/homeassistant/components/epic_games_store/coordinator.py b/homeassistant/components/epic_games_store/coordinator.py new file mode 100644 index 00000000000..d9c48f5da02 --- /dev/null +++ b/homeassistant/components/epic_games_store/coordinator.py @@ -0,0 +1,81 @@ +"""The Epic Games Store integration data coordinator.""" + +from __future__ import annotations + +from datetime import timedelta +import logging +from typing import Any + +from epicstore_api import EpicGamesStoreAPI + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_COUNTRY, CONF_LANGUAGE +from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import DOMAIN, CalendarType +from .helper import format_game_data + +SCAN_INTERVAL = timedelta(days=1) + +_LOGGER = logging.getLogger(__name__) + + +class EGSCalendarUpdateCoordinator( + DataUpdateCoordinator[dict[str, list[dict[str, Any]]]] +): + """Class to manage fetching data from the Epic Game Store.""" + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + """Initialize.""" + self._api = EpicGamesStoreAPI( + entry.data[CONF_LANGUAGE], + entry.data[CONF_COUNTRY], + ) + self.language = entry.data[CONF_LANGUAGE] + + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=SCAN_INTERVAL, + ) + + async def _async_update_data(self) -> dict[str, list[dict[str, Any]]]: + """Update data via library.""" + raw_data = await self.hass.async_add_executor_job(self._api.get_free_games) + _LOGGER.debug(raw_data) + data = raw_data["data"]["Catalog"]["searchStore"]["elements"] + + discount_games = filter( + lambda game: game.get("promotions") + and ( + # Current discount(s) + game["promotions"]["promotionalOffers"] + or + # Upcoming discount(s) + game["promotions"]["upcomingPromotionalOffers"] + ), + data, + ) + + return_data: dict[str, list[dict[str, Any]]] = { + CalendarType.DISCOUNT: [], + CalendarType.FREE: [], + } + for discount_game in discount_games: + game = format_game_data(discount_game, self.language) + + if game["discount_type"]: + return_data[game["discount_type"]].append(game) + + return_data[CalendarType.DISCOUNT] = sorted( + return_data[CalendarType.DISCOUNT], + key=lambda game: game["discount_start_at"], + ) + return_data[CalendarType.FREE] = sorted( + return_data[CalendarType.FREE], key=lambda game: game["discount_start_at"] + ) + + _LOGGER.debug(return_data) + return return_data diff --git a/homeassistant/components/epic_games_store/helper.py b/homeassistant/components/epic_games_store/helper.py new file mode 100644 index 00000000000..2510c7699e5 --- /dev/null +++ b/homeassistant/components/epic_games_store/helper.py @@ -0,0 +1,92 @@ +"""Helper for Epic Games Store.""" + +import contextlib +from typing import Any + +from homeassistant.util import dt as dt_util + + +def format_game_data(raw_game_data: dict[str, Any], language: str) -> dict[str, Any]: + """Format raw API game data for Home Assistant users.""" + img_portrait = None + img_landscape = None + + for image in raw_game_data["keyImages"]: + if image["type"] == "OfferImageTall": + img_portrait = image["url"] + if image["type"] == "OfferImageWide": + img_landscape = image["url"] + + current_promotions = raw_game_data["promotions"]["promotionalOffers"] + upcoming_promotions = raw_game_data["promotions"]["upcomingPromotionalOffers"] + + promotion_data = {} + if ( + current_promotions + and raw_game_data["price"]["totalPrice"]["discountPrice"] == 0 + ): + promotion_data = current_promotions[0]["promotionalOffers"][0] + else: + promotion_data = (current_promotions or upcoming_promotions)[0][ + "promotionalOffers" + ][0] + + return { + "title": raw_game_data["title"].replace("\xa0", " "), + "description": raw_game_data["description"].strip().replace("\xa0", " "), + "released_at": dt_util.parse_datetime(raw_game_data["effectiveDate"]), + "original_price": raw_game_data["price"]["totalPrice"]["fmtPrice"][ + "originalPrice" + ].replace("\xa0", " "), + "publisher": raw_game_data["seller"]["name"], + "url": get_game_url(raw_game_data, language), + "img_portrait": img_portrait, + "img_landscape": img_landscape, + "discount_type": ("free" if is_free_game(raw_game_data) else "discount") + if promotion_data + else None, + "discount_start_at": dt_util.parse_datetime(promotion_data["startDate"]) + if promotion_data + else None, + "discount_end_at": dt_util.parse_datetime(promotion_data["endDate"]) + if promotion_data + else None, + } + + +def get_game_url(raw_game_data: dict[str, Any], language: str) -> str: + """Format raw API game data for Home Assistant users.""" + url_bundle_or_product = "bundles" if raw_game_data["offerType"] == "BUNDLE" else "p" + url_slug: str | None = None + try: + url_slug = raw_game_data["offerMappings"][0]["pageSlug"] + except Exception: # pylint: disable=broad-except + with contextlib.suppress(Exception): + url_slug = raw_game_data["catalogNs"]["mappings"][0]["pageSlug"] + + if not url_slug: + url_slug = raw_game_data["urlSlug"] + + return f"https://store.epicgames.com/{language}/{url_bundle_or_product}/{url_slug}" + + +def is_free_game(game: dict[str, Any]) -> bool: + """Return if the game is free or will be free.""" + return ( + # Current free game(s) + game["promotions"]["promotionalOffers"] + and game["promotions"]["promotionalOffers"][0]["promotionalOffers"][0][ + "discountSetting" + ]["discountPercentage"] + == 0 + and + # Checking current price, maybe not necessary + game["price"]["totalPrice"]["discountPrice"] == 0 + ) or ( + # Upcoming free game(s) + game["promotions"]["upcomingPromotionalOffers"] + and game["promotions"]["upcomingPromotionalOffers"][0]["promotionalOffers"][0][ + "discountSetting" + ]["discountPercentage"] + == 0 + ) diff --git a/homeassistant/components/epic_games_store/manifest.json b/homeassistant/components/epic_games_store/manifest.json new file mode 100644 index 00000000000..665eaec6668 --- /dev/null +++ b/homeassistant/components/epic_games_store/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "epic_games_store", + "name": "Epic Games Store", + "codeowners": ["@hacf-fr", "@Quentame"], + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/epic_games_store", + "integration_type": "service", + "iot_class": "cloud_polling", + "requirements": ["epicstore-api==0.1.7"] +} diff --git a/homeassistant/components/epic_games_store/strings.json b/homeassistant/components/epic_games_store/strings.json new file mode 100644 index 00000000000..58a87a55f81 --- /dev/null +++ b/homeassistant/components/epic_games_store/strings.json @@ -0,0 +1,38 @@ +{ + "config": { + "step": { + "user": { + "data": { + "language": "Language", + "country": "Country" + } + } + }, + "error": { + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_service%]" + } + }, + "entity": { + "calendar": { + "free_games": { + "name": "Free games", + "state_attributes": { + "games": { + "name": "Games" + } + } + }, + "discount_games": { + "name": "Discount games", + "state_attributes": { + "games": { + "name": "[%key:component::epic_games_store::entity::calendar::free_games::state_attributes::games::name%]" + } + } + } + } + } +} diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index c02d8a2987e..e5d5f37ad5a 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -152,6 +152,7 @@ FLOWS = { "enocean", "enphase_envoy", "environment_canada", + "epic_games_store", "epion", "epson", "eq3btsmart", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 2b1e5b4fb91..0ee796d5376 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -1649,6 +1649,12 @@ "config_flow": false, "iot_class": "local_polling" }, + "epic_games_store": { + "name": "Epic Games Store", + "integration_type": "service", + "config_flow": true, + "iot_class": "cloud_polling" + }, "epion": { "name": "Epion", "integration_type": "hub", diff --git a/requirements_all.txt b/requirements_all.txt index dade5079fbd..055db11d63a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -806,6 +806,9 @@ env-canada==0.6.0 # homeassistant.components.season ephem==4.1.5 +# homeassistant.components.epic_games_store +epicstore-api==0.1.7 + # homeassistant.components.epion epion==0.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d1bfeff488f..ff19a6a5c89 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -660,6 +660,9 @@ env-canada==0.6.0 # homeassistant.components.season ephem==4.1.5 +# homeassistant.components.epic_games_store +epicstore-api==0.1.7 + # homeassistant.components.epion epion==0.0.3 diff --git a/tests/components/epic_games_store/__init__.py b/tests/components/epic_games_store/__init__.py new file mode 100644 index 00000000000..1c5baf3704f --- /dev/null +++ b/tests/components/epic_games_store/__init__.py @@ -0,0 +1 @@ +"""Tests for the Epic Games Store integration.""" diff --git a/tests/components/epic_games_store/common.py b/tests/components/epic_games_store/common.py new file mode 100644 index 00000000000..95191ad97f9 --- /dev/null +++ b/tests/components/epic_games_store/common.py @@ -0,0 +1,31 @@ +"""Common methods used across tests for Epic Games Store.""" + +from unittest.mock import patch + +from homeassistant.components.epic_games_store.const import DOMAIN +from homeassistant.const import CONF_COUNTRY, CONF_LANGUAGE +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from .const import MOCK_COUNTRY, MOCK_LANGUAGE + +from tests.common import MockConfigEntry + + +async def setup_platform(hass: HomeAssistant, platform: str) -> MockConfigEntry: + """Set up the Epic Games Store platform.""" + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + }, + unique_id=f"freegames-{MOCK_LANGUAGE}-{MOCK_COUNTRY}", + ) + mock_entry.add_to_hass(hass) + + with patch("homeassistant.components.epic_games_store.PLATFORMS", [platform]): + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + return mock_entry diff --git a/tests/components/epic_games_store/conftest.py b/tests/components/epic_games_store/conftest.py new file mode 100644 index 00000000000..e02997a429e --- /dev/null +++ b/tests/components/epic_games_store/conftest.py @@ -0,0 +1,44 @@ +"""Define fixtures for Epic Games Store tests.""" + +from unittest.mock import Mock, patch + +import pytest + +from .const import ( + DATA_ERROR_ATTRIBUTE_NOT_FOUND, + DATA_FREE_GAMES, + DATA_FREE_GAMES_CHRISTMAS_SPECIAL, +) + + +@pytest.fixture(name="service_multiple") +def mock_service_multiple(): + """Mock a successful service with multiple free & discount games.""" + with patch( + "homeassistant.components.epic_games_store.coordinator.EpicGamesStoreAPI" + ) as service_mock: + instance = service_mock.return_value + instance.get_free_games = Mock(return_value=DATA_FREE_GAMES) + yield service_mock + + +@pytest.fixture(name="service_christmas_special") +def mock_service_christmas_special(): + """Mock a successful service with Christmas special case.""" + with patch( + "homeassistant.components.epic_games_store.coordinator.EpicGamesStoreAPI" + ) as service_mock: + instance = service_mock.return_value + instance.get_free_games = Mock(return_value=DATA_FREE_GAMES_CHRISTMAS_SPECIAL) + yield service_mock + + +@pytest.fixture(name="service_attribute_not_found") +def mock_service_attribute_not_found(): + """Mock a successful service returning a not found attribute error with free & discount games.""" + with patch( + "homeassistant.components.epic_games_store.coordinator.EpicGamesStoreAPI" + ) as service_mock: + instance = service_mock.return_value + instance.get_free_games = Mock(return_value=DATA_ERROR_ATTRIBUTE_NOT_FOUND) + yield service_mock diff --git a/tests/components/epic_games_store/const.py b/tests/components/epic_games_store/const.py new file mode 100644 index 00000000000..dcd82c7e03e --- /dev/null +++ b/tests/components/epic_games_store/const.py @@ -0,0 +1,25 @@ +"""Test constants.""" + +from homeassistant.components.epic_games_store.const import DOMAIN + +from tests.common import load_json_object_fixture + +MOCK_LANGUAGE = "fr" +MOCK_COUNTRY = "FR" + +DATA_ERROR_ATTRIBUTE_NOT_FOUND = load_json_object_fixture( + "error_1004_attribute_not_found.json", DOMAIN +) + +DATA_ERROR_WRONG_COUNTRY = load_json_object_fixture( + "error_5222_wrong_country.json", DOMAIN +) + +# free games +DATA_FREE_GAMES = load_json_object_fixture("free_games.json", DOMAIN) + +DATA_FREE_GAMES_ONE = load_json_object_fixture("free_games_one.json", DOMAIN) + +DATA_FREE_GAMES_CHRISTMAS_SPECIAL = load_json_object_fixture( + "free_games_christmas_special.json", DOMAIN +) diff --git a/tests/components/epic_games_store/fixtures/error_1004_attribute_not_found.json b/tests/components/epic_games_store/fixtures/error_1004_attribute_not_found.json new file mode 100644 index 00000000000..6cb14608c2b --- /dev/null +++ b/tests/components/epic_games_store/fixtures/error_1004_attribute_not_found.json @@ -0,0 +1,1026 @@ +{ + "errors": [ + { + "message": "CatalogOffer/offerMappings: Request failed with status code 404", + "locations": [ + { + "line": 73, + "column": 17 + } + ], + "correlationId": "0451aa13-b1d6-4f90-8ca5-d12bf917675a", + "serviceResponse": "{\"errorMessage\":\"The item or resource being requested could not be found.\",\"errorCode\":\"errors.com.epicgames.not_found\",\"numericErrorCode\":1004,\"errorStatus\":404}", + "stack": null, + "path": ["Catalog", "searchStore", "elements", 4, "offerMappings"] + }, + { + "message": "CatalogNamespace/mappings: Request failed with status code 404", + "locations": [ + { + "line": 68, + "column": 19 + } + ], + "correlationId": "0451aa13-b1d6-4f90-8ca5-d12bf917675a", + "serviceResponse": "{\"errorMessage\":\"The item or resource being requested could not be found.\",\"errorCode\":\"errors.com.epicgames.not_found\",\"numericErrorCode\":1004,\"errorStatus\":404}", + "stack": null, + "path": ["Catalog", "searchStore", "elements", 4, "catalogNs", "mappings"] + } + ], + "data": { + "Catalog": { + "searchStore": { + "elements": [ + { + "title": "Godlike Burger", + "id": "d9300ace164b41ac90a7b54e59d47953", + "namespace": "beb7e64d3da74ae780405da48cccb581", + "description": "Dans Godlike Burger, vous g\u00e9rez le restaurant le plus d\u00e9ment de la galaxie\u00a0! Assommez, empoisonnez et tuez les clients... pour les transformer en steaks\u00a0! Mais nulle crainte\u00a0: la client\u00e8le alien reviendra si vous la jouez fine, car c'est trop bon de s'adonner au cannibalisme.", + "effectiveDate": "2022-04-21T17:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "viewableDate": "2022-03-28T18:00:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/f42598038b9343e58d27e0a8c0b831b6/godlike-burger-offer-1trpc.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/f42598038b9343e58d27e0a8c0b831b6/download-godlike-burger-offer-8u2uh.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/f42598038b9343e58d27e0a8c0b831b6/download-godlike-burger-offer-8u2uh.jpg" + } + ], + "seller": { + "id": "o-d2ygr9bjcjfebgt8842wvvbmswympz", + "name": "Daedalic Entertainment" + }, + "productSlug": null, + "urlSlug": "37b001690e2a4d6f872567cdd06f0c6f", + "url": null, + "items": [ + { + "id": "c027f1bc9db54f189ad938634500e542", + "namespace": "beb7e64d3da74ae780405da48cccb581" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "false" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + } + ], + "tags": [ + { + "id": "1216" + }, + { + "id": "21894" + }, + { + "id": "19847" + }, + { + "id": "1083" + }, + { + "id": "9547" + }, + { + "id": "9549" + }, + { + "id": "1263" + }, + { + "id": "10719" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "godlike-burger-4150a0", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "godlike-burger-4150a0", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 1999, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "19,99\u00a0\u20ac", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "1c2dc8194022428da305eedb42ed574d", + "endDate": "2023-10-12T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-10-05T15:00:00.000Z", + "endDate": "2023-10-12T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Destiny\u00a02\u00a0: Pack 30e anniversaire Bungie", + "id": "e7b9e222c7274dd28714aba2e06d2a01", + "namespace": "428115def4ca4deea9d69c99c5a5a99e", + "description": "Le Pack 30e anniversaire inclut un nouveau donjon, le lance-roquettes exotique Gjallarhorn, de nouvelles armes et armures, et plus encore. ", + "effectiveDate": "2022-08-23T13:00:00.000Z", + "offerType": "DLC", + "expiryDate": null, + "viewableDate": "2022-08-08T15:00:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/FR_Bungie_Bungie_30th_Anniversary_Pack_S4_1200x1600_1200x1600-04ebd49752c682d003014680f3d5be18" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/FR_Bungie_Bungie_30th_Anniversary_Pack_S3_2560x1440_2560x1440-b2f882323923927c414ab23faf1022ca" + }, + { + "type": "ProductLogo", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/FR_Bungie_Bungie_30th_Anniversary_Pack_OfferLogo_200x200_200x200-234225abe0aca2bfa7f5c5bc6e6fe348" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/FR_Bungie_Bungie_30th_Anniversary_Pack_S4_1200x1600_1200x1600-04ebd49752c682d003014680f3d5be18" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/FR_Bungie_Bungie_30th_Anniversary_Pack_S3_2560x1440_2560x1440-b2f882323923927c414ab23faf1022ca" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot1_1920x1080-37c070caa0106b08910518150bf96e94" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot2_1920x1080-14490e3ec01dceedce23d870774b2393" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot3_1920x1080-fdf882ad2cc98be7e63516b4ad28d6e9" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot4_1920x1080-079d4e12a8a04b31f7d4def7f4b745e7" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot5_1920x1080-f3c958c685629b6678544cba8bffc483" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot6_1920x1080-f13bb310baf9c158d15d473474c11586" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot7_1920x1080-6d2b714d2cfd64623cdcc39487d0b429" + }, + { + "type": "featuredMedia", + "url": "https://cdn1.epicgames.com/offer/428115def4ca4deea9d69c99c5a5a99e/Screenshot8_1920x1080-0956ff1a3a4969d9a3f2b96d87bdc19d" + } + ], + "seller": { + "id": "o-49lqsefbl6zr5sy3ztak77ej97cuvh", + "name": "Bungie" + }, + "productSlug": null, + "urlSlug": "destiny-2--bungie-30th-anniversary-pack", + "url": null, + "items": [ + { + "id": "904b57fb8bcd41a6be6c690a92ab3c15", + "namespace": "428115def4ca4deea9d69c99c5a5a99e" + } + ], + "customAttributes": [], + "categories": [ + { + "path": "addons" + }, + { + "path": "freegames" + }, + { + "path": "addons/durable" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1203" + }, + { + "id": "1210" + }, + { + "id": "1370" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "destiny-2", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "destiny-2--bungie-30th-anniversary-pack", + "pageType": "addon--cms-hybrid" + } + ], + "price": { + "totalPrice": { + "discountPrice": 2499, + "originalPrice": 2499, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "24,99\u00a0\u20ac", + "discountPrice": "24,99\u00a0\u20ac", + "intermediatePrice": "24,99\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-10-11T16:00:00.000Z", + "endDate": "2023-10-25T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 60 + } + } + ] + } + ] + } + }, + { + "title": "Gloomhaven", + "id": "9232fdbc352445cc820a54bdc97ed2bb", + "namespace": "bc079f73f020432fac896d30c8e2c330", + "description": "Que vous soyez arriv\u00e9s \u00e0 Gloomhaven en r\u00e9pondant \u00e0 l'appel de l'aventure ou au d\u00e9sir cupide de l'\u00e9clat de l'or, votre destin n'en sera pas chang\u00e9...", + "effectiveDate": "2022-09-22T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "viewableDate": "2022-09-22T15:00:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/ef2777467a3c49059a076e42fd9b41f0/gloomhaven-offer-1j9mc.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/ef2777467a3c49059a076e42fd9b41f0/download-gloomhaven-offer-1ho2x.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/ef2777467a3c49059a076e42fd9b41f0/download-gloomhaven-offer-1ho2x.jpg" + } + ], + "seller": { + "id": "o-4x4bpaww55p5g3f6xpyqe2cneqxd5d", + "name": "Asmodee" + }, + "productSlug": null, + "urlSlug": "0d48da287df14493a7415b560ec1bbb3", + "url": null, + "items": [ + { + "id": "6047532dd78a456593d0ffd6602a7218", + "namespace": "bc079f73f020432fac896d30c8e2c330" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetViewableDate", + "value": "true" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + }, + { + "key": "isBlockchainUsed", + "value": "false" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "games" + } + ], + "tags": [ + { + "id": "29088" + }, + { + "id": "21122" + }, + { + "id": "1188" + }, + { + "id": "21127" + }, + { + "id": "19847" + }, + { + "id": "21129" + }, + { + "id": "1386" + }, + { + "id": "9547" + }, + { + "id": "9549" + }, + { + "id": "1264" + }, + { + "id": "21137" + }, + { + "id": "21138" + }, + { + "id": "21139" + }, + { + "id": "16979" + }, + { + "id": "21140" + }, + { + "id": "21141" + }, + { + "id": "1367" + }, + { + "id": "22776" + }, + { + "id": "1370" + }, + { + "id": "1115" + }, + { + "id": "21147" + }, + { + "id": "21149" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "gloomhaven-92f741", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "gloomhaven-92f741", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 3499, + "originalPrice": 3499, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "34,99\u00a0\u20ac", + "discountPrice": "34,99\u00a0\u20ac", + "intermediatePrice": "34,99\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + }, + { + "title": "911 Operator", + "id": "268fd6ea355740d6ba4c76c3ffd4cbe0", + "namespace": "d923c737f0d243ccab407605ea40d39e", + "description": "911 OPERATOR est un jeu o\u00f9 tu deviens op\u00e9rateur de la ligne des urgences et o\u00f9 tu r\u00e9sous des incidents en fournissant des instruction et en g\u00e9rant des \u00e9quipes de secours. Tu peux jouer sur la carte de n\u2019importe quelle ville* du monde!", + "effectiveDate": "2023-09-14T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "viewableDate": "2023-09-07T15:00:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/c06cc46c27954f55974e9e7a4f3b3849/911-operator-omkv7.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/c06cc46c27954f55974e9e7a4f3b3849/911-operator-8dcp7.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/c06cc46c27954f55974e9e7a4f3b3849/911-operator-8dcp7.jpg" + } + ], + "seller": { + "id": "o-8dv8wz77w8tqnymmm8e99p28eny7kg", + "name": "Games Operators S.A." + }, + "productSlug": null, + "urlSlug": "ecb09cc5f55345e6bf6d3d9354c12876", + "url": null, + "items": [ + { + "id": "07499df5530b45c3ad8464a96cbe26c7", + "namespace": "d923c737f0d243ccab407605ea40d39e" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetViewableDate", + "value": "true" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + }, + { + "key": "isBlockchainUsed", + "value": "false" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + } + ], + "tags": [ + { + "id": "1393" + }, + { + "id": "19847" + }, + { + "id": "1370" + }, + { + "id": "1115" + }, + { + "id": "9547" + }, + { + "id": "1263" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "911-operator-585edd", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "911-operator-585edd", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 1349, + "originalPrice": 1349, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "13,49\u00a0\u20ac", + "discountPrice": "13,49\u00a0\u20ac", + "intermediatePrice": "13,49\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-10-23T14:00:00.000Z", + "endDate": "2023-10-30T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 50 + } + } + ] + } + ] + } + }, + { + "title": "Q.U.B.E. ULTIMATE BUNDLE", + "id": "f18f14a76a874aa883a651fcc8c513d0", + "namespace": "0712c5eca64b47bbbced82cabba9f0d7", + "description": "Q.U.B.E. ULTIMATE BUNDLE", + "effectiveDate": "2023-10-12T15:00:00.000Z", + "offerType": "BUNDLE", + "expiryDate": null, + "viewableDate": "2023-10-05T14:25:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/0712c5eca64b47bbbced82cabba9f0d7/EGSBundle_Portrait_V2_1200x1600-981ac683de50fd5afed2c87dbc26494a" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/0712c5eca64b47bbbced82cabba9f0d7/EGSBundle_Landscape_V2_2560x1440-50dbecaa32e134e246717f8a5e60ad25" + }, + { + "type": "ProductLogo", + "url": "https://cdn1.epicgames.com/offer/0712c5eca64b47bbbced82cabba9f0d7/EGSBundle_Logo_V2_400x400-99dcb7d141728efbe2b1b4e993ce6339" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/0712c5eca64b47bbbced82cabba9f0d7/EGSBundle_Portrait_V2_1200x1600-981ac683de50fd5afed2c87dbc26494a" + } + ], + "seller": { + "id": "o-kk34ewvmscclj5a2ukx49ff6qknn7a", + "name": "Ten Hut Games" + }, + "productSlug": "qube-ultimate-bundle", + "urlSlug": "qube-ultimate-bundle", + "url": null, + "items": [ + { + "id": "11d229f51ac1445a8925b8d14da82b9b", + "namespace": "ad43401ad02840c2b2bee5f1f1a59988" + }, + { + "id": "0e7ec1d579ab481c93dff6056c19299f", + "namespace": "4b5f1eb366dc45f0920d397c01b291ba" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.productSlug", + "value": "qube-ultimate-bundle" + } + ], + "categories": [ + { + "path": "bundles" + }, + { + "path": "freegames" + }, + { + "path": "bundles/games" + } + ], + "tags": [ + { + "id": "1216" + }, + { + "id": "1298" + }, + { + "id": "1203" + }, + { + "id": "1117" + }, + { + "id": "1294" + } + ], + "catalogNs": { + "mappings": null + }, + "offerMappings": null, + "price": { + "totalPrice": { + "discountPrice": 4499, + "originalPrice": 4499, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "44,99\u00a0\u20ac", + "discountPrice": "44,99\u00a0\u20ac", + "intermediatePrice": "44,99\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-10-12T15:00:00.000Z", + "endDate": "2023-10-19T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ] + } + }, + { + "title": "PAYDAY 2", + "id": "de434b7be57940d98ede93b50cdacfc2", + "namespace": "d5241c76f178492ea1540fce45616757", + "description": "PAYDAY 2 is an action-packed, four-player co-op shooter that once again lets gamers don the masks of the original PAYDAY crew - Dallas, Hoxton, Wolf and Chains - as they descend on Washington DC for an epic crime spree.", + "effectiveDate": "2099-01-01T00:00:00.000Z", + "offerType": "OTHERS", + "expiryDate": null, + "viewableDate": "2023-06-01T14:25:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": true, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/mammoth-h1nvv_2560x1440-ac346d6ece5ec356561e112fbddb2dc1" + }, + { + "type": "VaultClosed", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/EN-mega-sale-vault-16x9-asset_1920x1080-a27cf3919dde320a72936374a1d47813" + } + ], + "seller": { + "id": "o-ufmrk5furrrxgsp5tdngefzt5rxdcn", + "name": "Epic Dev Test Account" + }, + "productSlug": "payday-2-c66369", + "urlSlug": "mystery-game-7", + "url": null, + "items": [ + { + "id": "8341d7c7e4534db7848cc428aa4cbe5a", + "namespace": "d5241c76f178492ea1540fce45616757" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.freegames.vault.close", + "value": "[]" + }, + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.slug", + "value": "sales-and-specials/mega-sale" + }, + { + "key": "com.epicgames.app.freegames.vault.open", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "payday-2-c66369" + } + ], + "categories": [ + { + "path": "freegames/vaulted" + }, + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "applications" + } + ], + "tags": [], + "catalogNs": { + "mappings": [] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + }, + { + "title": "Blazing Sails", + "id": "363d0be3b57d4741a046d38da0e6355e", + "namespace": "aee7dd76aa6746578f476dc47f8d1d7f", + "description": "Survivez \u00e0 Blazing Sails, un jeu de pirate en JcJ tr\u00e9pidant\u00a0! Cr\u00e9ez votre navire et vos pirates uniques. Naviguez en \u00e9quipe avec d'autres joueurs\u00a0! D\u00e9couvrez diff\u00e9rents modes de jeu, cartes, armes, types de navires et bien plus encore. Battez les \u00e9quipages adverses dans d'\u00e9piques combats sur terre et en mer\u00a0!", + "effectiveDate": "2099-04-06T17:35:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "viewableDate": "2023-03-30T15:00:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/aee7dd76aa6746578f476dc47f8d1d7f/EGS_BlazingSails_GetUpGames_S2_1200x1600-bae3831e97b560958dc785e830ebed8c" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/aee7dd76aa6746578f476dc47f8d1d7f/EGS_BlazingSails_GetUpGames_S1_2560x1440-fd7a7b3d357555880cb7969634553c5b" + }, + { + "type": "ProductLogo", + "url": "https://cdn1.epicgames.com/offer/aee7dd76aa6746578f476dc47f8d1d7f/EGS_BlazingSails_GetUpGames_IC1_400x400-a7b91f257fcbd9ced825d3da95298170" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/aee7dd76aa6746578f476dc47f8d1d7f/EGS_BlazingSails_GetUpGames_S2_1200x1600-bae3831e97b560958dc785e830ebed8c" + } + ], + "seller": { + "id": "o-ftmts7pjfvdywkby885rdzl4hdbtys", + "name": "Iceberg Interactive" + }, + "productSlug": "blazing-sails", + "urlSlug": "blazing-sails", + "url": null, + "items": [ + { + "id": "30aec28f450a41499dd27e0d27294b56", + "namespace": "aee7dd76aa6746578f476dc47f8d1d7f" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "KR" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "blazing-sails" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1216" + }, + { + "id": "1264" + }, + { + "id": "1203" + }, + { + "id": "9547" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "blazing-sails", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 1479, + "originalPrice": 1479, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "14,79\u00a0\u20ac", + "discountPrice": "14,79\u00a0\u20ac", + "intermediatePrice": "14,79\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-10-12T15:00:00.000Z", + "endDate": "2023-10-19T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ] + } + } + ], + "paging": { + "count": 1000, + "total": 7 + } + } + } + }, + "extensions": {} +} diff --git a/tests/components/epic_games_store/fixtures/error_5222_wrong_country.json b/tests/components/epic_games_store/fixtures/error_5222_wrong_country.json new file mode 100644 index 00000000000..c91d5551ff9 --- /dev/null +++ b/tests/components/epic_games_store/fixtures/error_5222_wrong_country.json @@ -0,0 +1,23 @@ +{ + "errors": [ + { + "message": "CatalogQuery/searchStore: Request failed with status code 400", + "locations": [ + { + "line": 18, + "column": 9 + } + ], + "correlationId": "e10ad58e-a4f9-4097-af5d-cafdbe0d8bbd", + "serviceResponse": "{\"errorCode\":\"errors.com.epicgames.catalog.invalid_country_code\",\"errorMessage\":\"Sorry the value you entered: en-US, does not appear to be a valid ISO country code.\",\"messageVars\":[\"en-US\"],\"numericErrorCode\":5222,\"originatingService\":\"com.epicgames.catalog.public\",\"intent\":\"prod\",\"errorStatus\":400}", + "stack": null, + "path": ["Catalog", "searchStore"] + } + ], + "data": { + "Catalog": { + "searchStore": null + } + }, + "extensions": {} +} diff --git a/tests/components/epic_games_store/fixtures/free_games.json b/tests/components/epic_games_store/fixtures/free_games.json new file mode 100644 index 00000000000..29ff43f32a0 --- /dev/null +++ b/tests/components/epic_games_store/fixtures/free_games.json @@ -0,0 +1,2189 @@ +{ + "data": { + "Catalog": { + "searchStore": { + "elements": [ + { + "title": "Rising Storm 2: Vietnam", + "id": "b19d810d322240e7b37bcf84ffac60ce", + "namespace": "3542a1df211e492bb2abecb7c734f7f9", + "description": "Red Orchestra Series' take on Vietnam: 64-player MP matches; 20+ maps; US Army & Marines, PAVN/NVA, NLF/VC; Australians and ARVN forces; 50+ weapons; 4 flyable helicopters; mines, traps and tunnels; Brutal. Authentic. Gritty. Character customization.", + "effectiveDate": "2020-10-08T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/3542a1df211e492bb2abecb7c734f7f9/offer/EGS_RisingStorm2Vietnam_AntimatterGamesTripwireInteractive_S3-2560x1440-e08edd93cb71bf15b50a74f3de2d17b0.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/3542a1df211e492bb2abecb7c734f7f9/offer/EGS_RisingStorm2Vietnam_AntimatterGamesTripwireInteractive_S4-1200x1600-5e3b2f8107e17cc008237e52761d67e5.jpg" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/3542a1df211e492bb2abecb7c734f7f9/offer/EGS_RisingStorm2Vietnam_AntimatterGamesTripwireInteractive_S3-2560x1440-e08edd93cb71bf15b50a74f3de2d17b0.jpg" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/3542a1df211e492bb2abecb7c734f7f9/offer/EGS_RisingStorm2Vietnam_AntimatterGamesTripwireInteractive_S4-1200x1600-5e3b2f8107e17cc008237e52761d67e5.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/3542a1df211e492bb2abecb7c734f7f9/offer/EGS_RisingStorm2Vietnam_AntimatterGamesTripwireInteractive_S4-1200x1600-5e3b2f8107e17cc008237e52761d67e5.jpg" + }, + { + "type": "CodeRedemption_340x440", + "url": "https://cdn1.epicgames.com/3542a1df211e492bb2abecb7c734f7f9/offer/EGS_RisingStorm2Vietnam_AntimatterGamesTripwireInteractive_S4-1200x1600-5e3b2f8107e17cc008237e52761d67e5.jpg" + } + ], + "seller": { + "id": "o-2baznhy8tfh7fmyb55ul656v7ggt7r", + "name": "Tripwire Interactive" + }, + "productSlug": "rising-storm-2-vietnam/home", + "urlSlug": "risingstorm2vietnam", + "url": null, + "items": [ + { + "id": "685765c3f37049c49b45bea4173725d2", + "namespace": "3542a1df211e492bb2abecb7c734f7f9" + }, + { + "id": "c7c6d65ac4cc4ef0ae12e8e89f134684", + "namespace": "3542a1df211e492bb2abecb7c734f7f9" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "publisherName", + "value": "Tripwire Interactive" + }, + { + "key": "developerName", + "value": "Antimatter Games" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "rising-storm-2-vietnam/home" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1216" + }, + { + "id": "21122" + }, + { + "id": "21125" + }, + { + "id": "21129" + }, + { + "id": "14346" + }, + { + "id": "9547" + }, + { + "id": "16011" + }, + { + "id": "15375" + }, + { + "id": "21135" + }, + { + "id": "21138" + }, + { + "id": "1299" + }, + { + "id": "16979" + }, + { + "id": "21139" + }, + { + "id": "21140" + }, + { + "id": "17493" + }, + { + "id": "21141" + }, + { + "id": "22485" + }, + { + "id": "18777" + }, + { + "id": "18778" + }, + { + "id": "1115" + }, + { + "id": "21148" + }, + { + "id": "21149" + }, + { + "id": "14944" + }, + { + "id": "19242" + }, + { + "id": "18607" + }, + { + "id": "1203" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "rising-storm-2-vietnam", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 2199, + "originalPrice": 2199, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac21.99", + "discountPrice": "\u20ac21.99", + "intermediatePrice": "\u20ac21.99" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-11-03T15:00:00.000Z", + "endDate": "2022-11-10T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ] + } + }, + { + "title": "Idle Champions of the Forgotten Realms", + "id": "a9748abde1c94b66aae5250bb9fc5503", + "namespace": "7e508f543b05465abe3a935960eb70ac", + "description": "Idle Champions is a licensed Dungeons & Dragons strategy management video game uniting iconic characters from novels, campaigns, and shows into one epic adventure.", + "effectiveDate": "2021-02-16T17:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/7e508f543b05465abe3a935960eb70ac/EGS_IdleChampionsoftheForgottenRealms_CodenameEntertainment_S2_1200x1600-dd9a8f25ad56089231f43cf639bde217" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/7e508f543b05465abe3a935960eb70ac/EGS_IdleChampionsoftheForgottenRealms_CodenameEntertainment_S1_2560x1440-e2a1ffd224f443594d5deff3a47a45e2" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/7e508f543b05465abe3a935960eb70ac/EGS_IdleChampionsoftheForgottenRealms_CodenameEntertainment_S2_1200x1600-dd9a8f25ad56089231f43cf639bde217" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/offer/7e508f543b05465abe3a935960eb70ac/EGS_IdleChampionsoftheForgottenRealms_CodenameEntertainment_S2_1200x1600-dd9a8f25ad56089231f43cf639bde217" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/7e508f543b05465abe3a935960eb70ac/EGS_IdleChampionsoftheForgottenRealms_CodenameEntertainment_S1_2560x1440-e2a1ffd224f443594d5deff3a47a45e2" + } + ], + "seller": { + "id": "o-3kpjwtwqwfl2p9wdwvpad7yqz4kt6c", + "name": "Codename Entertainment" + }, + "productSlug": "idle-champions-of-the-forgotten-realms", + "urlSlug": "banegeneralaudience", + "url": null, + "items": [ + { + "id": "9a4e1a1eb6b140f6a9e5e4dcb5a2bf55", + "namespace": "7e508f543b05465abe3a935960eb70ac" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "KR" + }, + { + "key": "publisherName", + "value": "Codename Entertainment" + }, + { + "key": "developerName", + "value": "Codename Entertainment" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "idle-champions-of-the-forgotten-realms" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "21136" + }, + { + "id": "21122" + }, + { + "id": "21138" + }, + { + "id": "21139" + }, + { + "id": "1188" + }, + { + "id": "1141" + }, + { + "id": "1370" + }, + { + "id": "1115" + }, + { + "id": "9547" + }, + { + "id": "21149" + }, + { + "id": "21119" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "idle-champions-of-the-forgotten-realms", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Hundred Days - Winemaking Simulator", + "id": "141eee80fbe041d48e16e7b998829295", + "namespace": "4d8b727a49144090b103f6b6ba471e71", + "description": "Winemaking could be your best adventure. Make the best wine interacting with soil and nature and take your winery to the top. Your beautiful journey into the winemaking tradition starts now.", + "effectiveDate": "2021-05-13T14:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/4d8b727a49144090b103f6b6ba471e71/offer/EGS_HundredDaysWinemakingSimulatorDEMO_BrokenArmsGames_Demo_G1C_00-1920x1080-0ffeb0645f0badb615627b481b4a913e.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/4d8b727a49144090b103f6b6ba471e71/offer/EGS_HundredDaysWinemakingSimulatorDEMO_BrokenArmsGames_Demo_S2-1200x1600-35531ec1fa868e3876fac76471a24017.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/4d8b727a49144090b103f6b6ba471e71/offer/EGS_HundredDaysWinemakingSimulatorDEMO_BrokenArmsGames_Demo_S2-1200x1600-35531ec1fa868e3876fac76471a24017.jpg" + }, + { + "type": "CodeRedemption_340x440", + "url": "https://cdn1.epicgames.com/4d8b727a49144090b103f6b6ba471e71/offer/EGS_HundredDaysWinemakingSimulatorDEMO_BrokenArmsGames_Demo_S2-1200x1600-35531ec1fa868e3876fac76471a24017.jpg" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/4d8b727a49144090b103f6b6ba471e71/offer/EGS_HundredDaysWinemakingSimulatorDEMO_BrokenArmsGames_Demo_S1-2560x1440-8f0dd95b6027cd1243361d430b3bf552.jpg" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/4d8b727a49144090b103f6b6ba471e71/offer/EGS_HundredDaysWinemakingSimulatorDEMO_BrokenArmsGames_Demo_S2-1200x1600-35531ec1fa868e3876fac76471a24017.jpg" + } + ], + "seller": { + "id": "o-ty5rvlnsbgdnfffytsywat86gcedkm", + "name": "Broken Arms Games srls" + }, + "productSlug": "hundred-days-winemaking-simulator", + "urlSlug": "hundred-days-winemaking-simulator", + "url": null, + "items": [ + { + "id": "03cacb8754f243bfbc536c9dda0eb32e", + "namespace": "4d8b727a49144090b103f6b6ba471e71" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "developerName", + "value": "Broken Arms Games" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "hundred-days-winemaking-simulator" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1188" + }, + { + "id": "21894" + }, + { + "id": "21127" + }, + { + "id": "19242" + }, + { + "id": "21130" + }, + { + "id": "16011" + }, + { + "id": "9547" + }, + { + "id": "1263" + }, + { + "id": "15375" + }, + { + "id": "18607" + }, + { + "id": "1393" + }, + { + "id": "21138" + }, + { + "id": "16979" + }, + { + "id": "21140" + }, + { + "id": "17493" + }, + { + "id": "21141" + }, + { + "id": "18777" + }, + { + "id": "1370" + }, + { + "id": "18778" + }, + { + "id": "21146" + }, + { + "id": "1115" + }, + { + "id": "21149" + }, + { + "id": "10719" + }, + { + "id": "21119" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "hundred-days-winemaking-simulator", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 1999, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac19.99", + "discountPrice": "\u20ac19.99", + "intermediatePrice": "\u20ac19.99" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + }, + { + "title": "Shadow of the Tomb Raider: Definitive Edition", + "id": "ee7f3c6725fd4fd4b8aeab8622cb770e", + "namespace": "4b5461ca8d1c488787b5200b420de066", + "description": "In Shadow of the Tomb Raider Definitive Edition experience the final chapter of Lara\u2019s origin as she is forged into the Tomb Raider she is destined to be.", + "effectiveDate": "2021-12-30T16:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "CodeRedemption_340x440", + "url": "https://cdn1.epicgames.com/offer/4b5461ca8d1c488787b5200b420de066/egs-shadowofthetombraiderdefinitiveedition-eidosmontralcrystaldynamicsnixxessoftware-s4-1200x1600-7ee40d6fa744_1200x1600-950cdb624cc75d04fe3c8c0b62ce98de" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/4b5461ca8d1c488787b5200b420de066/egs-shadowofthetombraiderdefinitiveedition-eidosmontralcrystaldynamicsnixxessoftware-s4-1200x1600-7ee40d6fa744_1200x1600-950cdb624cc75d04fe3c8c0b62ce98de" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/4b5461ca8d1c488787b5200b420de066/egs-shadowofthetombraiderdefinitiveedition-eidosmontralcrystaldynamicsnixxessoftware-s1-2560x1440-eca6506e95a1_2560x1440-193582a5fd76a593804e0171d6395cf4" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/4b5461ca8d1c488787b5200b420de066/egs-shadowofthetombraiderdefinitiveedition-eidosmontralcrystaldynamicsnixxessoftware-s4-1200x1600-7ee40d6fa744_1200x1600-950cdb624cc75d04fe3c8c0b62ce98de" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/offer/4b5461ca8d1c488787b5200b420de066/egs-shadowofthetombraiderdefinitiveedition-eidosmontralcrystaldynamicsnixxessoftware-s4-1200x1600-7ee40d6fa744_1200x1600-950cdb624cc75d04fe3c8c0b62ce98de" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/4b5461ca8d1c488787b5200b420de066/egs-shadowofthetombraiderdefinitiveedition-eidosmontralcrystaldynamicsnixxessoftware-s1-2560x1440-eca6506e95a1_2560x1440-193582a5fd76a593804e0171d6395cf4" + } + ], + "seller": { + "id": "o-7petn7mrlk8g86ktqm7uglcr7lfaja", + "name": "Square Enix" + }, + "productSlug": "shadow-of-the-tomb-raider", + "urlSlug": "shadow-of-the-tomb-raider", + "url": null, + "items": [ + { + "id": "e7f90759e0544e42be9391d10a5c6000", + "namespace": "4b5461ca8d1c488787b5200b420de066" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "shadow-of-the-tomb-raider" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1216" + }, + { + "id": "21122" + }, + { + "id": "18051" + }, + { + "id": "1188" + }, + { + "id": "21894" + }, + { + "id": "21127" + }, + { + "id": "9547" + }, + { + "id": "9549" + }, + { + "id": "21138" + }, + { + "id": "21139" + }, + { + "id": "21140" + }, + { + "id": "21109" + }, + { + "id": "21141" + }, + { + "id": "22485" + }, + { + "id": "1370" + }, + { + "id": "21146" + }, + { + "id": "1117" + }, + { + "id": "21149" + }, + { + "id": "21119" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "shadow-of-the-tomb-raider", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 1319, + "originalPrice": 3999, + "voucherDiscount": 0, + "discount": 2680, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac39.99", + "discountPrice": "\u20ac13.19", + "intermediatePrice": "\u20ac13.19" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "35111a3c715340d08910a9f6a5b3e846", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-10-18T15:00:00.000Z", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 33 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Terraforming Mars", + "id": "f2496286331e405793d69807755b7b23", + "namespace": "25d726130e6c4fe68f88e71933bda955", + "description": "The taming of the Red Planet has begun!\n\nControl your corporation, play project cards, build up production, place your cities and green areas on the map, and race for milestones and awards!\n\nWill your corporation lead the way into humanity's new era?", + "effectiveDate": "2022-05-05T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/5199b206e46947ebad5e5c282e95776f/terraforming-mars-offer-1j70f.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/5199b206e46947ebad5e5c282e95776f/download-terraforming-mars-offer-13t2e.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/5199b206e46947ebad5e5c282e95776f/download-terraforming-mars-offer-13t2e.jpg" + } + ], + "seller": { + "id": "o-4x4bpaww55p5g3f6xpyqe2cneqxd5d", + "name": "Asmodee" + }, + "productSlug": null, + "urlSlug": "24cdfcde68bf4a7e8b8618ac2c0c460b", + "url": null, + "items": [ + { + "id": "ee49486d7346465dba1f1dec85725aee", + "namespace": "25d726130e6c4fe68f88e71933bda955" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "games" + } + ], + "tags": [ + { + "id": "18051" + }, + { + "id": "1188" + }, + { + "id": "21125" + }, + { + "id": "1386" + }, + { + "id": "9547" + }, + { + "id": "21138" + }, + { + "id": "1203" + }, + { + "id": "1299" + }, + { + "id": "21139" + }, + { + "id": "21140" + }, + { + "id": "21141" + }, + { + "id": "1370" + }, + { + "id": "1115" + }, + { + "id": "21148" + }, + { + "id": "21149" + }, + { + "id": "10719" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "terraforming-mars-18c3ad", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "terraforming-mars-18c3ad", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 1399, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 600, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac19.99", + "discountPrice": "\u20ac13.99", + "intermediatePrice": "\u20ac13.99" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "8e9732952e714f6583416e66fc451cd7", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-10-18T15:00:00.000Z", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 70 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Car Mechanic Simulator 2018", + "id": "5eb27cf1747c40b5a0d4f5492774678d", + "namespace": "226306adde104c9092247dcd4bfa1499", + "description": "Build and expand your repair service empire in this incredibly detailed and highly realistic simulation game, where attention to car detail is astonishing. Find classic, unique cars in the new Barn Find module and Junkyard module.", + "effectiveDate": "2022-06-23T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/226306adde104c9092247dcd4bfa1499/EGS_CarMechanicSimulator2018_RedDotGames_S2_1200x1600-f285924f9144353f57ac4631f0c689e6" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/226306adde104c9092247dcd4bfa1499/EGS_CarMechanicSimulator2018_RedDotGames_S1_2560x1440-3489ef1499e64c168fdf4b14926d2c23" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/226306adde104c9092247dcd4bfa1499/EGS_CarMechanicSimulator2018_RedDotGames_S2_1200x1600-f285924f9144353f57ac4631f0c689e6" + } + ], + "seller": { + "id": "o-5n5cbrasl5yzexjc529rypg8eh8lfb", + "name": "PlayWay" + }, + "productSlug": "car-mechanic-simulator-2018", + "urlSlug": "car-mechanic-simulator-2018", + "url": null, + "items": [ + { + "id": "49a3a8597c4240ecaf1f9068106c9869", + "namespace": "226306adde104c9092247dcd4bfa1499" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "car-mechanic-simulator-2018" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "21120" + }, + { + "id": "1188" + }, + { + "id": "21127" + }, + { + "id": "9547" + }, + { + "id": "1393" + }, + { + "id": "21138" + }, + { + "id": "21139" + }, + { + "id": "21140" + }, + { + "id": "21141" + }, + { + "id": "1370" + }, + { + "id": "21146" + }, + { + "id": "21148" + }, + { + "id": "21149" + }, + { + "id": "21119" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "car-mechanic-simulator-2018", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 1599, + "originalPrice": 1599, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac15.99", + "discountPrice": "\u20ac15.99", + "intermediatePrice": "\u20ac15.99" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + }, + { + "title": "A Game Of Thrones: The Board Game Digital Edition", + "id": "a125d72a47a1490aba78c4e79a40395d", + "namespace": "1b737464d3c441f8956315433be02d3b", + "description": "It is the digital adaptation of the top-selling strategy board game from Fantasy Flight Games.", + "effectiveDate": "2022-06-23T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/61c1413e3db0423f9ddd4a5edbee717e/a-game-of-thrones-offer-11gxu.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/61c1413e3db0423f9ddd4a5edbee717e/download-a-game-of-thrones-offer-1q8ei.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/61c1413e3db0423f9ddd4a5edbee717e/download-a-game-of-thrones-offer-1q8ei.jpg" + } + ], + "seller": { + "id": "o-4x4bpaww55p5g3f6xpyqe2cneqxd5d", + "name": "Asmodee" + }, + "productSlug": null, + "urlSlug": "ce6f7ab4edab4cc2aa7e0ff4c19540e2", + "url": null, + "items": [ + { + "id": "dc6ae31efba7401fa72ed93f0bd37c6a", + "namespace": "1b737464d3c441f8956315433be02d3b" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "games" + } + ], + "tags": [ + { + "id": "18051" + }, + { + "id": "1188" + }, + { + "id": "21125" + }, + { + "id": "9547" + }, + { + "id": "21138" + }, + { + "id": "1203" + }, + { + "id": "1299" + }, + { + "id": "21139" + }, + { + "id": "21140" + }, + { + "id": "21141" + }, + { + "id": "1370" + }, + { + "id": "1115" + }, + { + "id": "21149" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "a-game-of-thrones-5858a3", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "a-game-of-thrones-5858a3", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 1399, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 600, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac19.99", + "discountPrice": "\u20ac13.99", + "intermediatePrice": "\u20ac13.99" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "689de276cf3245a7bffdfa0d20500150", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-10-18T15:00:00.000Z", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 70 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Filament", + "id": "296453e71c884f95aecf4d582cf66915", + "namespace": "89fb09a222a54e53b692e9c36e68d0a1", + "description": "Solve challenging cable-based puzzles and uncover what really happened to the crew of The Alabaster. Now with Hint System (for those ultra tricky puzzles).", + "effectiveDate": "2022-08-11T11:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/5a72e62648d747189d2f5e7abb47444c/filament-offer-qrwye.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/5a72e62648d747189d2f5e7abb47444c/download-filament-offer-mk58q.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/5a72e62648d747189d2f5e7abb47444c/download-filament-offer-mk58q.jpg" + } + ], + "seller": { + "id": "o-fnqgc5v2xczx9fgawvcejwj88z2mnx", + "name": "Kasedo Games Ltd" + }, + "productSlug": null, + "urlSlug": "323de464947e4ee5a035c525b6b78021", + "url": null, + "items": [ + { + "id": "d4fa1325ef014725a89cc40e9b99e43d", + "namespace": "89fb09a222a54e53b692e9c36e68d0a1" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "games" + } + ], + "tags": [ + { + "id": "1298" + }, + { + "id": "21894" + }, + { + "id": "19847" + }, + { + "id": "1370" + }, + { + "id": "9547" + }, + { + "id": "9549" + }, + { + "id": "1263" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "filament-332a92", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "filament-332a92", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 1699, + "originalPrice": 1699, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac16.99", + "discountPrice": "\u20ac16.99", + "intermediatePrice": "\u20ac16.99" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-11-03T15:00:00.000Z", + "endDate": "2022-11-10T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ] + } + }, + { + "title": "Warhammer 40,000: Mechanicus - Standard Edition", + "id": "559b16fa81134dce83b5b8b7cf67b5b3", + "namespace": "144f9e231e2846d1a4381d9bb678f69d", + "description": "Take control of the most technologically advanced army in the Imperium - The Adeptus Mechanicus. Your every decision will weigh heavily on the outcome of the mission, in this turn-based tactical game. Will you be blessed by the Omnissiah?", + "effectiveDate": "2022-08-11T11:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/d26f2f9ea65c462dbd39040ae8389d36/warhammer-mechanicus-offer-17fnz.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/d26f2f9ea65c462dbd39040ae8389d36/download-warhammer-mechanicus-offer-1f6bv.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/d26f2f9ea65c462dbd39040ae8389d36/download-warhammer-mechanicus-offer-1f6bv.jpg" + } + ], + "seller": { + "id": "o-fnqgc5v2xczx9fgawvcejwj88z2mnx", + "name": "Kasedo Games Ltd" + }, + "productSlug": null, + "urlSlug": "f37159d9bd96489ab1b99bdad1ee796c", + "url": null, + "items": [ + { + "id": "f923ad9f3428472ab67baa4618c205a0", + "namespace": "144f9e231e2846d1a4381d9bb678f69d" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "games" + } + ], + "tags": [ + { + "id": "21894" + }, + { + "id": "19847" + }, + { + "id": "1386" + }, + { + "id": "1115" + }, + { + "id": "9547" + }, + { + "id": "9549" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "warhammer-mechanicus-0e4b71", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "warhammer-mechanicus-0e4b71", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 2999, + "voucherDiscount": 0, + "discount": 2999, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac29.99", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "7a3ee39632f5458990b6a9ad295881b8", + "endDate": "2022-11-03T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-10-27T15:00:00.000Z", + "endDate": "2022-11-03T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Fallout 3: Game of the Year Edition", + "id": "d6f01b1827c64ed388191ae507fe7c1b", + "namespace": "fa702d34a37248ba98fb17f680c085e3", + "description": "Prepare for the Future\u2122\nExperience the most acclaimed game of 2008 like never before with Fallout 3: Game of the Year Edition. Create a character of your choosing and descend into a post-apocalyptic world where every minute is a fight for survival", + "effectiveDate": "2022-10-20T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/fa702d34a37248ba98fb17f680c085e3/EGS_Fallout3GameoftheYearEdition_BethesdaGameStudios_S2_1200x1600-e2ba392652a1f57c4feb65d6bbd1f963" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/fa702d34a37248ba98fb17f680c085e3/EGS_Fallout3GameoftheYearEdition_BethesdaGameStudios_S1_2560x1440-073f5b4cf358f437a052a3c29806efa0" + }, + { + "type": "ProductLogo", + "url": "https://cdn1.epicgames.com/offer/fa702d34a37248ba98fb17f680c085e3/EGS_Fallout3GameoftheYearEdition_BethesdaGameStudios_IC1_400x400-5e37dfe1d35c9ccf25c8889fe7218613" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/fa702d34a37248ba98fb17f680c085e3/EGS_Fallout3GameoftheYearEdition_BethesdaGameStudios_S2_1200x1600-e2ba392652a1f57c4feb65d6bbd1f963" + } + ], + "seller": { + "id": "o-bthbhn6wd7fzj73v5p4436ucn3k37u", + "name": "Bethesda Softworks LLC" + }, + "productSlug": "fallout-3-game-of-the-year-edition", + "urlSlug": "fallout-3-game-of-the-year-edition", + "url": null, + "items": [ + { + "id": "6b750e631e414927bde5b3e13b647443", + "namespace": "fa702d34a37248ba98fb17f680c085e3" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "fallout-3-game-of-the-year-edition" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "21122" + }, + { + "id": "1188" + }, + { + "id": "21894" + }, + { + "id": "21127" + }, + { + "id": "9547" + }, + { + "id": "21137" + }, + { + "id": "21138" + }, + { + "id": "21139" + }, + { + "id": "21140" + }, + { + "id": "21141" + }, + { + "id": "1367" + }, + { + "id": "1370" + }, + { + "id": "1307" + }, + { + "id": "21147" + }, + { + "id": "21148" + }, + { + "id": "1117" + }, + { + "id": "21149" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "fallout-3-game-of-the-year-edition", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 659, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 1340, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac19.99", + "discountPrice": "\u20ac6.59", + "intermediatePrice": "\u20ac6.59" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "779554ee7a604b0091a4335a60b6e55a", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-10-27T15:00:00.000Z", + "endDate": "2022-11-01T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 33 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Evoland Legendary Edition", + "id": "e068e168886a4a90a4e36a310e3bda32", + "namespace": "3f7bd21610f743e598fa8e955500f5b7", + "description": "Evoland Legendary Edition brings you two great and unique RPGs, with their graphic style and gameplay changing as you progress through the game!", + "effectiveDate": "2022-10-20T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/aafde465b31e4bd5a169ff1c8a164a17/evoland-legendary-edition-1y7m0.png" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/aafde465b31e4bd5a169ff1c8a164a17/evoland-legendary-edition-1j93v.png" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/aafde465b31e4bd5a169ff1c8a164a17/evoland-legendary-edition-1j93v.png" + } + ], + "seller": { + "id": "o-ealhln64lfep9ww929uq9qcdmbyfn4", + "name": "Shiro Games SAS" + }, + "productSlug": null, + "urlSlug": "224c60bb93864e1c8a1900bcf7d661dd", + "url": null, + "items": [ + { + "id": "c829f27d0ab0406db8edf2b97562ee93", + "namespace": "3f7bd21610f743e598fa8e955500f5b7" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition" + }, + { + "path": "games" + }, + { + "path": "games/edition/base" + } + ], + "tags": [ + { + "id": "1216" + }, + { + "id": "21109" + }, + { + "id": "1367" + }, + { + "id": "1370" + }, + { + "id": "9547" + }, + { + "id": "1117" + }, + { + "id": "9549" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "evoland-legendary-edition-5753ec", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "evoland-legendary-edition-5753ec", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 1999, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac19.99", + "discountPrice": "\u20ac19.99", + "intermediatePrice": "\u20ac19.99" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + }, + { + "title": "Saturnalia", + "id": "275d5915ebd2479f983f51025b22a1b8", + "namespace": "c749cd78da34408d8434a46271f4bb79", + "description": "A Survival Horror Adventure: as an ensemble cast, explore an isolated village of ancient ritual \u2013 its labyrinthine roads change each time you lose all your characters.", + "effectiveDate": "2022-10-27T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "CodeRedemption_340x440", + "url": "https://cdn1.epicgames.com/offer/c749cd78da34408d8434a46271f4bb79/EGS_Saturnalia_SantaRagione_S4_1200x1600-2216ff4aa6997dfb13d8bd4c6f2fa99e" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/offer/c749cd78da34408d8434a46271f4bb79/EGS_Saturnalia_SantaRagione_S4_1200x1600-2216ff4aa6997dfb13d8bd4c6f2fa99e" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/c749cd78da34408d8434a46271f4bb79/EGS_Saturnalia_SantaRagione_S3_2560x1440-3cd916a7260b77c8488f8f2b0f3a51ab" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/c749cd78da34408d8434a46271f4bb79/EGS_Saturnalia_SantaRagione_S4_1200x1600-2216ff4aa6997dfb13d8bd4c6f2fa99e" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/c749cd78da34408d8434a46271f4bb79/EGS_Saturnalia_SantaRagione_S3_2560x1440-3cd916a7260b77c8488f8f2b0f3a51ab" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/c749cd78da34408d8434a46271f4bb79/EGS_Saturnalia_SantaRagione_S4_1200x1600-2216ff4aa6997dfb13d8bd4c6f2fa99e" + } + ], + "seller": { + "id": "o-cjwnkas5rn476tzk72fbh2ftutnc2y", + "name": "Santa Ragione" + }, + "productSlug": "saturnalia", + "urlSlug": "saturnalia", + "url": null, + "items": [ + { + "id": "dbce8ecb6923490c9404529651251216", + "namespace": "c749cd78da34408d8434a46271f4bb79" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.productSlug", + "value": "saturnalia" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1218" + }, + { + "id": "19847" + }, + { + "id": "1080" + }, + { + "id": "1370" + }, + { + "id": "9547" + }, + { + "id": "1117" + }, + { + "id": "10719" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "saturnalia", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 1999, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "\u20ac19.99", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "8fa8f62eac9e4cab9fe242987c0f0988", + "endDate": "2022-11-03T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2022-10-27T15:00:00.000Z", + "endDate": "2022-11-03T15:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Maneater", + "id": "a22a7af179c54b86a93f3193ace8f7f4", + "namespace": "d5241c76f178492ea1540fce45616757", + "description": "Maneater", + "effectiveDate": "2099-01-01T00:00:00.000Z", + "offerType": "OTHERS", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": true, + "keyImages": [ + { + "type": "VaultClosed", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-tease-generic-promo-1920x1080_1920x1080-f7742c265e217510835ed14e04c48b4b" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-tease-generic-promo-1920x1080_1920x1080-f7742c265e217510835ed14e04c48b4b" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-carousel-mobile-thumbnail-1200x1600_1200x1600-1f45bf1ceb21c1ca2947f6df5ece5346" + }, + { + "type": "VaultOpened", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-w4-1920x1080_1920x1080-2df36fe63c18ff6fcb5febf3dd7ed06e" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-w4-1920x1080_1920x1080-2df36fe63c18ff6fcb5febf3dd7ed06e" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-w4-1920x1080_1920x1080-2df36fe63c18ff6fcb5febf3dd7ed06e" + } + ], + "seller": { + "id": "o-ufmrk5furrrxgsp5tdngefzt5rxdcn", + "name": "Epic Dev Test Account" + }, + "productSlug": "maneater", + "urlSlug": "game-4", + "url": null, + "items": [ + { + "id": "8341d7c7e4534db7848cc428aa4cbe5a", + "namespace": "d5241c76f178492ea1540fce45616757" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.freegames.vault.close", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.slug", + "value": "free-games" + }, + { + "key": "com.epicgames.app.freegames.vault.open", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "maneater" + } + ], + "categories": [ + { + "path": "freegames/vaulted" + }, + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "applications" + } + ], + "tags": [], + "catalogNs": { + "mappings": [] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + }, + { + "title": "Wolfenstein: The New Order", + "id": "1d41b93230e54bdd80c559d72adb7f4f", + "namespace": "d5241c76f178492ea1540fce45616757", + "description": "Wolfenstein: The New Order", + "effectiveDate": "2099-01-01T00:00:00.000Z", + "offerType": "OTHERS", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": true, + "keyImages": [ + { + "type": "VaultClosed", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-tease-generic-promo-1920x1080_1920x1080-f7742c265e217510835ed14e04c48b4b" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-carousel-mobile-thumbnail-1200x1600_1200x1600-1f45bf1ceb21c1ca2947f6df5ece5346" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-w3-1920x1080_1920x1080-4a501d33fb4ac641e3e1e290dcc0e6c1" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-w3-1920x1080_1920x1080-4a501d33fb4ac641e3e1e290dcc0e6c1" + }, + { + "type": "VaultOpened", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/egs-vault-w3-1920x1080_1920x1080-4a501d33fb4ac641e3e1e290dcc0e6c1" + } + ], + "seller": { + "id": "o-ufmrk5furrrxgsp5tdngefzt5rxdcn", + "name": "Epic Dev Test Account" + }, + "productSlug": "wolfenstein-the-new-order", + "urlSlug": "game-3", + "url": null, + "items": [ + { + "id": "8341d7c7e4534db7848cc428aa4cbe5a", + "namespace": "d5241c76f178492ea1540fce45616757" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.freegames.vault.close", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.slug", + "value": "free-games" + }, + { + "key": "com.epicgames.app.freegames.vault.open", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "wolfenstein-the-new-order" + } + ], + "categories": [ + { + "path": "freegames/vaulted" + }, + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "applications" + } + ], + "tags": [], + "catalogNs": { + "mappings": [] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + } + ], + "paging": { + "count": 1000, + "total": 14 + } + } + } + }, + "extensions": {} +} diff --git a/tests/components/epic_games_store/fixtures/free_games_christmas_special.json b/tests/components/epic_games_store/fixtures/free_games_christmas_special.json new file mode 100644 index 00000000000..0c65f47d3a0 --- /dev/null +++ b/tests/components/epic_games_store/fixtures/free_games_christmas_special.json @@ -0,0 +1,253 @@ +{ + "data": { + "Catalog": { + "searchStore": { + "elements": [ + { + "title": "Cursed to Golf", + "id": "0e4551e4ae65492b88009f8a4e41d778", + "namespace": "d5241c76f178492ea1540fce45616757", + "description": "Cursed to Golf", + "effectiveDate": "2023-12-27T16:00:00.000Z", + "offerType": "OTHERS", + "expiryDate": "2023-12-28T16:00:00.000Z", + "viewableDate": "2023-12-26T15:25:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": true, + "keyImages": [ + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/Free-Game-9_1920x1080-418a8fa10dd305bb2a219a7ec869c5ef" + }, + { + "type": "VaultClosed", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/Free-Game-9-teaser_1920x1080-e71ae0041736db5ac259a355cb301116" + } + ], + "seller": { + "id": "o-ufmrk5furrrxgsp5tdngefzt5rxdcn", + "name": "Epic Dev Test Account" + }, + "productSlug": "cursed-to-golf-a6bc22", + "urlSlug": "mysterygame-9", + "url": null, + "items": [ + { + "id": "8341d7c7e4534db7848cc428aa4cbe5a", + "namespace": "d5241c76f178492ea1540fce45616757" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.freegames.vault.close", + "value": "[]" + }, + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.slug", + "value": "sales-and-specials/holiday-sale" + }, + { + "key": "com.epicgames.app.freegames.vault.open", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "cursed-to-golf-a6bc22" + } + ], + "categories": [ + { + "path": "freegames/vaulted" + }, + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "applications" + } + ], + "tags": [], + "catalogNs": { + "mappings": [] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-12-27T16:00:00.000Z", + "endDate": "2023-12-28T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + }, + { + "startDate": "2023-12-27T16:00:00.000Z", + "endDate": "2023-12-28T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ], + "upcomingPromotionalOffers": [] + } + }, + { + "title": "Mystery Game Day 10", + "id": "a8c3537a579943a688e3bd355ae36209", + "namespace": "d5241c76f178492ea1540fce45616757", + "description": "Mystery Game Day 10", + "effectiveDate": "2099-01-01T16:00:00.000Z", + "offerType": "OTHERS", + "expiryDate": null, + "viewableDate": "2023-12-27T15:25:00.000Z", + "status": "ACTIVE", + "isCodeRedemptionOnly": true, + "keyImages": [ + { + "type": "VaultClosed", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/Free-Game-10-teaser_1920x1080-3ea48042a44263bf1a0a59c725b6d95b" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/Free-Game-10-teaser_1920x1080-3ea48042a44263bf1a0a59c725b6d95b" + } + ], + "seller": { + "id": "o-ufmrk5furrrxgsp5tdngefzt5rxdcn", + "name": "Epic Dev Test Account" + }, + "productSlug": "[]", + "urlSlug": "mysterygame-10", + "url": null, + "items": [ + { + "id": "8341d7c7e4534db7848cc428aa4cbe5a", + "namespace": "d5241c76f178492ea1540fce45616757" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.freegames.vault.close", + "value": "[]" + }, + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.slug", + "value": "sales-and-specials/holiday-sale" + }, + { + "key": "com.epicgames.app.freegames.vault.open", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "[]" + } + ], + "categories": [ + { + "path": "freegames/vaulted" + }, + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "applications" + } + ], + "tags": [], + "catalogNs": { + "mappings": [] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-12-28T16:00:00.000Z", + "endDate": "2023-12-29T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ] + } + } + ], + "paging": { + "count": 1000, + "total": 2 + } + } + } + }, + "extensions": {} +} diff --git a/tests/components/epic_games_store/fixtures/free_games_one.json b/tests/components/epic_games_store/fixtures/free_games_one.json new file mode 100644 index 00000000000..48cd64f68d4 --- /dev/null +++ b/tests/components/epic_games_store/fixtures/free_games_one.json @@ -0,0 +1,658 @@ +{ + "data": { + "Catalog": { + "searchStore": { + "elements": [ + { + "title": "Borderlands 3 Season Pass", + "id": "c3913a91e07b43cfbbbcfd8244c86dcc", + "namespace": "catnip", + "description": "Prolongez votre aventure dans Borderlands\u00a03 avec le Season Pass, regroupant des \u00e9l\u00e9ments cosm\u00e9tiques exclusifs et quatre histoires additionnelles, pour encore plus de missions et de d\u00e9fis\u00a0!", + "effectiveDate": "2019-09-11T12:00:00.000Z", + "offerType": "DLC", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/offer/catnip/Diesel_productv2_borderlands-3_season-pass_BL3_SEASONPASS_Hero-3840x2160-4411e63a005a43811a2bc516ae7ec584598fd4aa-3840x2160-b8988ebb0f3d9159671e8968af991f30_3840x2160-b8988ebb0f3d9159671e8968af991f30" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/offer/catnip/2KGMKT_BL3_Season_Pass_EGS_1200x1600_1200x1600-a7438a079c5576d328a74b9121278075" + }, + { + "type": "CodeRedemption_340x440", + "url": "https://cdn1.epicgames.com/offer/catnip/2KGMKT_BL3_Season_Pass_EGS_1200x1600_1200x1600-a7438a079c5576d328a74b9121278075" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/offer/catnip/2KGMKT_BL3_Season_Pass_EGS_1200x1600_1200x1600-a7438a079c5576d328a74b9121278075" + } + ], + "seller": { + "id": "o-37m6jbj5wcvrcvm4wusv7nazdfvbjk", + "name": "2K Games, Inc." + }, + "productSlug": "borderlands-3/season-pass", + "urlSlug": "borderlands-3--season-pass", + "url": null, + "items": [ + { + "id": "e9fdc1a9f47b4a5e8e63841c15de2b12", + "namespace": "catnip" + }, + { + "id": "fbc46bb6056940d2847ee1e80037a9af", + "namespace": "catnip" + }, + { + "id": "ff8e1152ddf742b68f9ac0cecd378917", + "namespace": "catnip" + }, + { + "id": "939e660825764e208938ab4f26b4da56", + "namespace": "catnip" + }, + { + "id": "4c43a9a691114ccd91c1884ab18f4e27", + "namespace": "catnip" + }, + { + "id": "3a6a3f9b351b4b599808df3267669b83", + "namespace": "catnip" + }, + { + "id": "ab030a9f53f3428fb2baf2ddbb0bb5ac", + "namespace": "catnip" + }, + { + "id": "ff96eef22b0e4c498e8ed80ac0030325", + "namespace": "catnip" + }, + { + "id": "5021e93a73374d6db1c1ce6c92234f8f", + "namespace": "catnip" + }, + { + "id": "9c0b1eb3265340678dff0fcb106402b1", + "namespace": "catnip" + }, + { + "id": "8c826db6e14f44aeac8816e1bd593632", + "namespace": "catnip" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.blacklist", + "value": "SA" + }, + { + "key": "publisherName", + "value": "2K" + }, + { + "key": "developerName", + "value": "Gearbox Software" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "borderlands-3/season-pass" + } + ], + "categories": [ + { + "path": "addons" + }, + { + "path": "freegames" + }, + { + "path": "addons/durable" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1264" + }, + { + "id": "16004" + }, + { + "id": "14869" + }, + { + "id": "26789" + }, + { + "id": "1367" + }, + { + "id": "1370" + }, + { + "id": "9547" + }, + { + "id": "9549" + }, + { + "id": "1294" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "borderlands-3", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "borderlands-3--season-pass", + "pageType": "addon--cms-hybrid" + } + ], + "price": { + "totalPrice": { + "discountPrice": 4999, + "originalPrice": 4999, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "49,99\u00a0\u20ac", + "discountPrice": "49,99\u00a0\u20ac", + "intermediatePrice": "49,99\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 30 + } + }, + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 25 + } + }, + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 25 + } + }, + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 30 + } + } + ] + } + ] + } + }, + { + "title": "Call of the Sea", + "id": "92da5d8d918543b6b408e36d9af81765", + "namespace": "5e427319eea1401ab20c6cd78a4163c4", + "description": "Call of the Sea is an otherworldly tale of mystery and love set in the 1930s South Pacific. Explore a lush island paradise, solve puzzles and unlock secrets in the hunt for your husband\u2019s missing expedition.", + "effectiveDate": "2022-02-17T15:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_CalloftheSea_OutoftheBlue_S1_2560x1440-204699c6410deef9c18be0ee392f8335" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_CalloftheSea_OutoftheBlue_S2_1200x1600-db63acf0c479c185e0ef8f8e73c8f0d8" + }, + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_CalloftheSea_OutoftheBlue_S5_1920x1080-7b22dfebdd9fcdde6e526c5dc4c16eb1" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_CalloftheSea_OutoftheBlue_S2_1200x1600-db63acf0c479c185e0ef8f8e73c8f0d8" + }, + { + "type": "CodeRedemption_340x440", + "url": "https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_CalloftheSea_OutoftheBlue_S2_1200x1600-db63acf0c479c185e0ef8f8e73c8f0d8" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_CalloftheSea_OutoftheBlue_S2_1200x1600-db63acf0c479c185e0ef8f8e73c8f0d8" + } + ], + "seller": { + "id": "o-fay4ghw9hhamujs53rfhy83ffexb7k", + "name": "Raw Fury" + }, + "productSlug": "call-of-the-sea", + "urlSlug": "call-of-the-sea", + "url": null, + "items": [ + { + "id": "cbc9c76c4bfc4bc6b28abb3afbcbf07a", + "namespace": "5e427319eea1401ab20c6cd78a4163c4" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.productSlug", + "value": "call-of-the-sea" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "games/edition" + }, + { + "path": "games/edition/base" + }, + { + "path": "applications" + } + ], + "tags": [ + { + "id": "1296" + }, + { + "id": "1298" + }, + { + "id": "21894" + }, + { + "id": "1370" + }, + { + "id": "9547" + }, + { + "id": "1117" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "call-of-the-sea", + "pageType": "productHome" + } + ] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 1999, + "originalPrice": 1999, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "19,99\u00a0\u20ac", + "discountPrice": "19,99\u00a0\u20ac", + "intermediatePrice": "19,99\u00a0\u20ac" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": { + "promotionalOffers": [], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 60 + } + }, + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + }, + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 60 + } + } + ] + } + ] + } + }, + { + "title": "Rise of Industry", + "id": "c04a2ab8ff4442cba0a41fb83453e701", + "namespace": "9f101e25b1a9427a9e6971d2b21c5f82", + "description": "Mettez vos comp\u00e9tences entrepreneuriales \u00e0 l'\u00e9preuve en cr\u00e9ant et en optimisant des cha\u00eenes de production complexes tout en gardant un \u0153il sur les r\u00e9sultats financiers. \u00c0 l'aube du 20e si\u00e8cle, appr\u00eatez-vous \u00e0 entrer dans un \u00e2ge d'or industriel, ou une d\u00e9pression historique.", + "effectiveDate": "2022-08-11T11:00:00.000Z", + "offerType": "BASE_GAME", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": false, + "keyImages": [ + { + "type": "OfferImageWide", + "url": "https://cdn1.epicgames.com/spt-assets/a6aeec29591b4b56b4383b4d2d7d0e1e/rise-of-industry-offer-1p22f.jpg" + }, + { + "type": "OfferImageTall", + "url": "https://cdn1.epicgames.com/spt-assets/a6aeec29591b4b56b4383b4d2d7d0e1e/download-rise-of-industry-offer-1uujr.jpg" + }, + { + "type": "Thumbnail", + "url": "https://cdn1.epicgames.com/spt-assets/a6aeec29591b4b56b4383b4d2d7d0e1e/download-rise-of-industry-offer-1uujr.jpg" + } + ], + "seller": { + "id": "o-fnqgc5v2xczx9fgawvcejwj88z2mnx", + "name": "Kasedo Games Ltd" + }, + "productSlug": null, + "urlSlug": "f88fedc022fe488caaedaa5c782ff90d", + "url": null, + "items": [ + { + "id": "9f5b48a778824e6aa330d2c1a47f41b2", + "namespace": "9f101e25b1a9427a9e6971d2b21c5f82" + } + ], + "customAttributes": [ + { + "key": "autoGeneratedPrice", + "value": "false" + }, + { + "key": "isManuallySetPCReleaseDate", + "value": "true" + } + ], + "categories": [ + { + "path": "freegames" + }, + { + "path": "games/edition/base" + }, + { + "path": "games/edition" + }, + { + "path": "games" + } + ], + "tags": [ + { + "id": "26789" + }, + { + "id": "19847" + }, + { + "id": "1370" + }, + { + "id": "1115" + }, + { + "id": "9547" + }, + { + "id": "10719" + } + ], + "catalogNs": { + "mappings": [ + { + "pageSlug": "rise-of-industry-0af838", + "pageType": "productHome" + } + ] + }, + "offerMappings": [ + { + "pageSlug": "rise-of-industry-0af838", + "pageType": "productHome" + } + ], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 2999, + "voucherDiscount": 0, + "discount": 2999, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "29,99\u00a0\u20ac", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [ + { + "id": "a19d30dc34f44923993e68b82b75a084", + "endDate": "2023-03-09T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE" + } + } + ] + } + ] + }, + "promotions": { + "promotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-03-02T16:00:00.000Z", + "endDate": "2023-03-09T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 0 + } + } + ] + } + ], + "upcomingPromotionalOffers": [ + { + "promotionalOffers": [ + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 25 + } + }, + { + "startDate": "2023-03-09T16:00:00.000Z", + "endDate": "2023-03-16T16:00:00.000Z", + "discountSetting": { + "discountType": "PERCENTAGE", + "discountPercentage": 25 + } + } + ] + } + ] + } + }, + { + "title": "Dishonored - Definitive Edition", + "id": "4d25d74b88d1474a8ab21ffb88ca6d37", + "namespace": "d5241c76f178492ea1540fce45616757", + "description": "Experience the definitive Dishonored collection. This complete compilation includes Dishonored as well as all of its additional content - Dunwall City Trials, The Knife of Dunwall, The Brigmore Witches and Void Walker\u2019s Arsenal.", + "effectiveDate": "2099-01-01T00:00:00.000Z", + "offerType": "OTHERS", + "expiryDate": null, + "status": "ACTIVE", + "isCodeRedemptionOnly": true, + "keyImages": [ + { + "type": "VaultClosed", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/15days-day15-wrapped-desktop-carousel-image_1920x1080-ebecfa7c79f02a9de5bca79560bee953" + }, + { + "type": "DieselStoreFrontWide", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/15days-day15-Unwrapped-desktop-carousel-image1_1920x1080-1992edb42bb8554ddeb14d430ba3f858" + }, + { + "type": "DieselStoreFrontTall", + "url": "https://cdn1.epicgames.com/offer/d5241c76f178492ea1540fce45616757/DAY15-carousel-mobile-unwrapped-image1_1200x1600-9716d77667d2a82931c55a4e4130989e" + } + ], + "seller": { + "id": "o-ufmrk5furrrxgsp5tdngefzt5rxdcn", + "name": "Epic Dev Test Account" + }, + "productSlug": "dishonored-definitive-edition", + "urlSlug": "mystery-game15", + "url": null, + "items": [ + { + "id": "8341d7c7e4534db7848cc428aa4cbe5a", + "namespace": "d5241c76f178492ea1540fce45616757" + } + ], + "customAttributes": [ + { + "key": "com.epicgames.app.freegames.vault.close", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.slug", + "value": "sales-and-specials/holiday-sale" + }, + { + "key": "com.epicgames.app.blacklist", + "value": "[]" + }, + { + "key": "com.epicgames.app.freegames.vault.open", + "value": "[]" + }, + { + "key": "com.epicgames.app.productSlug", + "value": "dishonored-definitive-edition" + } + ], + "categories": [ + { + "path": "freegames/vaulted" + }, + { + "path": "freegames" + }, + { + "path": "games" + }, + { + "path": "applications" + } + ], + "tags": [], + "catalogNs": { + "mappings": [] + }, + "offerMappings": [], + "price": { + "totalPrice": { + "discountPrice": 0, + "originalPrice": 0, + "voucherDiscount": 0, + "discount": 0, + "currencyCode": "EUR", + "currencyInfo": { + "decimals": 2 + }, + "fmtPrice": { + "originalPrice": "0", + "discountPrice": "0", + "intermediatePrice": "0" + } + }, + "lineOffers": [ + { + "appliedRules": [] + } + ] + }, + "promotions": null + } + ], + "paging": { + "count": 1000, + "total": 4 + } + } + } + }, + "extensions": {} +} diff --git a/tests/components/epic_games_store/test_calendar.py b/tests/components/epic_games_store/test_calendar.py new file mode 100644 index 00000000000..46ca974f85c --- /dev/null +++ b/tests/components/epic_games_store/test_calendar.py @@ -0,0 +1,162 @@ +"""Tests for the Epic Games Store calendars.""" + +from unittest.mock import Mock + +from freezegun.api import FrozenDateTimeFactory + +from homeassistant.components.calendar import ( + DOMAIN as CALENDAR_DOMAIN, + EVENT_END_DATETIME, + EVENT_START_DATETIME, + SERVICE_GET_EVENTS, +) +from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.util import dt as dt_util + +from .common import setup_platform + +from tests.common import async_fire_time_changed + + +async def test_setup_component(hass: HomeAssistant, service_multiple: Mock) -> None: + """Test setup component.""" + await setup_platform(hass, CALENDAR_DOMAIN) + + state = hass.states.get("calendar.epic_games_store_discount_games") + assert state.name == "Epic Games Store Discount games" + state = hass.states.get("calendar.epic_games_store_free_games") + assert state.name == "Epic Games Store Free games" + + +async def test_discount_games( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + service_multiple: Mock, +) -> None: + """Test discount games calendar.""" + freezer.move_to("2022-10-15T00:00:00.000Z") + + await setup_platform(hass, CALENDAR_DOMAIN) + + state = hass.states.get("calendar.epic_games_store_discount_games") + assert state.state == STATE_OFF + + freezer.move_to("2022-10-30T00:00:00.000Z") + async_fire_time_changed(hass) + + state = hass.states.get("calendar.epic_games_store_discount_games") + assert state.state == STATE_ON + + cal_attrs = dict(state.attributes) + assert cal_attrs == { + "friendly_name": "Epic Games Store Discount games", + "message": "Shadow of the Tomb Raider: Definitive Edition", + "all_day": False, + "start_time": "2022-10-18 08:00:00", + "end_time": "2022-11-01 08:00:00", + "location": "", + "description": "In Shadow of the Tomb Raider Definitive Edition experience the final chapter of Lara\u2019s origin as she is forged into the Tomb Raider she is destined to be.\n\nhttps://store.epicgames.com/fr/p/shadow-of-the-tomb-raider", + } + + +async def test_free_games( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + service_multiple: Mock, +) -> None: + """Test free games calendar.""" + freezer.move_to("2022-10-30T00:00:00.000Z") + + await setup_platform(hass, CALENDAR_DOMAIN) + + state = hass.states.get("calendar.epic_games_store_free_games") + assert state.state == STATE_ON + + cal_attrs = dict(state.attributes) + assert cal_attrs == { + "friendly_name": "Epic Games Store Free games", + "message": "Warhammer 40,000: Mechanicus - Standard Edition", + "all_day": False, + "start_time": "2022-10-27 08:00:00", + "end_time": "2022-11-03 08:00:00", + "location": "", + "description": "Take control of the most technologically advanced army in the Imperium - The Adeptus Mechanicus. Your every decision will weigh heavily on the outcome of the mission, in this turn-based tactical game. Will you be blessed by the Omnissiah?\n\nhttps://store.epicgames.com/fr/p/warhammer-mechanicus-0e4b71", + } + + +async def test_attribute_not_found( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + service_attribute_not_found: Mock, +) -> None: + """Test setup calendars with attribute not found error.""" + freezer.move_to("2023-10-12T00:00:00.000Z") + + await setup_platform(hass, CALENDAR_DOMAIN) + + state = hass.states.get("calendar.epic_games_store_discount_games") + assert state.name == "Epic Games Store Discount games" + state = hass.states.get("calendar.epic_games_store_free_games") + assert state.name == "Epic Games Store Free games" + assert state.state == STATE_ON + + +async def test_christmas_special( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + service_christmas_special: Mock, +) -> None: + """Test setup calendars with Christmas special case.""" + freezer.move_to("2023-12-28T00:00:00.000Z") + + await setup_platform(hass, CALENDAR_DOMAIN) + + state = hass.states.get("calendar.epic_games_store_discount_games") + assert state.name == "Epic Games Store Discount games" + assert state.state == STATE_OFF + + state = hass.states.get("calendar.epic_games_store_free_games") + assert state.name == "Epic Games Store Free games" + assert state.state == STATE_ON + + +async def test_get_events( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + service_multiple: Mock, +) -> None: + """Test setup component with calendars.""" + freezer.move_to("2022-10-30T00:00:00.000Z") + + await setup_platform(hass, CALENDAR_DOMAIN) + + # 1 week in range of data + result = await hass.services.async_call( + CALENDAR_DOMAIN, + SERVICE_GET_EVENTS, + { + ATTR_ENTITY_ID: ["calendar.epic_games_store_discount_games"], + EVENT_START_DATETIME: dt_util.parse_datetime("2022-10-20T00:00:00.000Z"), + EVENT_END_DATETIME: dt_util.parse_datetime("2022-10-27T00:00:00.000Z"), + }, + blocking=True, + return_response=True, + ) + + assert len(result["calendar.epic_games_store_discount_games"]["events"]) == 3 + + # 1 week out of range of data + result = await hass.services.async_call( + CALENDAR_DOMAIN, + SERVICE_GET_EVENTS, + { + ATTR_ENTITY_ID: ["calendar.epic_games_store_discount_games"], + EVENT_START_DATETIME: dt_util.parse_datetime("1970-01-01T00:00:00.000Z"), + EVENT_END_DATETIME: dt_util.parse_datetime("1970-01-08T00:00:00.000Z"), + }, + blocking=True, + return_response=True, + ) + + assert len(result["calendar.epic_games_store_discount_games"]["events"]) == 0 diff --git a/tests/components/epic_games_store/test_config_flow.py b/tests/components/epic_games_store/test_config_flow.py new file mode 100644 index 00000000000..83e9cf9e99e --- /dev/null +++ b/tests/components/epic_games_store/test_config_flow.py @@ -0,0 +1,142 @@ +"""Test the Epic Games Store config flow.""" + +from http.client import HTTPException +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.epic_games_store.config_flow import get_default_language +from homeassistant.components.epic_games_store.const import DOMAIN +from homeassistant.const import CONF_COUNTRY, CONF_LANGUAGE +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType + +from .const import ( + DATA_ERROR_ATTRIBUTE_NOT_FOUND, + DATA_ERROR_WRONG_COUNTRY, + DATA_FREE_GAMES, + MOCK_COUNTRY, + MOCK_LANGUAGE, +) + + +async def test_default_language(hass: HomeAssistant) -> None: + """Test we get the form.""" + hass.config.language = "fr" + hass.config.country = "FR" + assert get_default_language(hass) == "fr" + + hass.config.language = "es" + hass.config.country = "ES" + assert get_default_language(hass) == "es-ES" + + hass.config.language = "en" + hass.config.country = "AZ" + assert get_default_language(hass) is None + + +async def test_form(hass: HomeAssistant) -> None: + """Test we get the form.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["errors"] is None + + with patch( + "homeassistant.components.epic_games_store.config_flow.EpicGamesStoreAPI.get_free_games", + return_value=DATA_FREE_GAMES, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["result"].unique_id == f"freegames-{MOCK_LANGUAGE}-{MOCK_COUNTRY}" + assert ( + result2["title"] + == f"Epic Games Store - Free Games ({MOCK_LANGUAGE}-{MOCK_COUNTRY})" + ) + assert result2["data"] == { + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + } + + +async def test_form_cannot_connect(hass: HomeAssistant) -> None: + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.epic_games_store.config_flow.EpicGamesStoreAPI.get_free_games", + side_effect=HTTPException, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + }, + ) + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "unknown"} + + +async def test_form_cannot_connect_wrong_param(hass: HomeAssistant) -> None: + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.epic_games_store.config_flow.EpicGamesStoreAPI.get_free_games", + return_value=DATA_ERROR_WRONG_COUNTRY, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + }, + ) + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "unknown"} + + +async def test_form_service_error(hass: HomeAssistant) -> None: + """Test we handle service error gracefully.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.epic_games_store.config_flow.EpicGamesStoreAPI.get_free_games", + return_value=DATA_ERROR_ATTRIBUTE_NOT_FOUND, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["result"].unique_id == f"freegames-{MOCK_LANGUAGE}-{MOCK_COUNTRY}" + assert ( + result2["title"] + == f"Epic Games Store - Free Games ({MOCK_LANGUAGE}-{MOCK_COUNTRY})" + ) + assert result2["data"] == { + CONF_LANGUAGE: MOCK_LANGUAGE, + CONF_COUNTRY: MOCK_COUNTRY, + } diff --git a/tests/components/epic_games_store/test_helper.py b/tests/components/epic_games_store/test_helper.py new file mode 100644 index 00000000000..155ccb7d211 --- /dev/null +++ b/tests/components/epic_games_store/test_helper.py @@ -0,0 +1,74 @@ +"""Tests for the Epic Games Store helpers.""" + +from typing import Any + +import pytest + +from homeassistant.components.epic_games_store.helper import ( + format_game_data, + get_game_url, + is_free_game, +) + +from .const import DATA_ERROR_ATTRIBUTE_NOT_FOUND, DATA_FREE_GAMES_ONE + +FREE_GAMES_API = DATA_FREE_GAMES_ONE["data"]["Catalog"]["searchStore"]["elements"] +FREE_GAME = FREE_GAMES_API[2] +NOT_FREE_GAME = FREE_GAMES_API[0] + + +def test_format_game_data() -> None: + """Test game data format.""" + game_data = format_game_data(FREE_GAME, "fr") + assert game_data + assert game_data["title"] + assert game_data["description"] + assert game_data["released_at"] + assert game_data["original_price"] + assert game_data["publisher"] + assert game_data["url"] + assert game_data["img_portrait"] + assert game_data["img_landscape"] + assert game_data["discount_type"] == "free" + assert game_data["discount_start_at"] + assert game_data["discount_end_at"] + + +@pytest.mark.parametrize( + ("raw_game_data", "expected_result"), + [ + ( + DATA_ERROR_ATTRIBUTE_NOT_FOUND["data"]["Catalog"]["searchStore"][ + "elements" + ][1], + "/p/destiny-2--bungie-30th-anniversary-pack", + ), + ( + DATA_ERROR_ATTRIBUTE_NOT_FOUND["data"]["Catalog"]["searchStore"][ + "elements" + ][4], + "/bundles/qube-ultimate-bundle", + ), + ( + DATA_ERROR_ATTRIBUTE_NOT_FOUND["data"]["Catalog"]["searchStore"][ + "elements" + ][5], + "/p/mystery-game-7", + ), + ], +) +def test_get_game_url(raw_game_data: dict[str, Any], expected_result: bool) -> None: + """Test to get the game URL.""" + assert get_game_url(raw_game_data, "fr").endswith(expected_result) + + +@pytest.mark.parametrize( + ("raw_game_data", "expected_result"), + [ + (FREE_GAME, True), + (NOT_FREE_GAME, False), + ], +) +def test_is_free_game(raw_game_data: dict[str, Any], expected_result: bool) -> None: + """Test if this game is free.""" + assert is_free_game(raw_game_data) == expected_result