Skip to content
4 changes: 2 additions & 2 deletions demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { csfd } from './src';
// csfd.setOptions({ optionsRequest: { credentials: 'include' } });

// Parse movie
csfd.movie(10135).then((movie) => console.log(movie));
csfd.movie(621073).then((movie) => console.log(movie));

// csfd.search('matrix').then((search) => console.log(search));
// csfd.cinema(1, 'today').then((cinema) => console.log(cinema));

// Parse creator
csfd.creator(2120).then((creator) => console.log(creator));
// csfd.creator(2120).then((creator) => console.log(creator));

/**
* USER RATINGS
Expand Down
18 changes: 17 additions & 1 deletion src/dto/movie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ export interface CSFDMovie extends CSFDScreening {
premieres: CSFDPremiere[];
related: CSFDMovieListItem[];
similar: CSFDMovieListItem[];
seasons: CSFDSeason[] | null;
episodes: CSFDSeason[] | null;
parent: CSFDParent | null;
episodeCode: string | null;
seasonName: string | null;
}

export interface CSFDParent {
season: { id: number; name: string } | null;
series: { id: number; name: string } | null;
}

export interface MovieJsonLd {
Expand Down Expand Up @@ -151,7 +161,6 @@ export type CSFDCreatorGroups =
| 'Scénografie'
| 'Kostýmy';


export type CSFDCreatorGroupsEnglish =
| 'Directed by'
| 'Screenplay'
Expand Down Expand Up @@ -187,3 +196,10 @@ export interface CSFDPremiere {
}

export type CSFDBoxContent = 'Související' | 'Podobné';

export interface CSFDSeason {
id: number;
name: string;
url: string;
info: string | null;
}
10 changes: 10 additions & 0 deletions src/helpers/global.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ export const parseIdFromUrl = (url: string): number => {
return +id || null;
};

export const parseLastIdFromUrl = (url: string): number => {
if (url) {
const idSlug = url?.split('/')[3];
const id = idSlug?.split('-')[0];
return +id || null;
} else {
return null;
}
};

export const getColor = (cls: string): CSFDColorRating => {
switch (cls) {
case 'page-lightgrey':
Expand Down
115 changes: 114 additions & 1 deletion src/helpers/movie.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,22 @@ import {
CSFDGenres,
CSFDMovieCreator,
CSFDMovieListItem,
CSFDParent,
CSFDPremiere,
CSFDSeason,
CSFDTitlesOther,
CSFDVod,
CSFDVodService,
MovieJsonLd
} from '../dto/movie';
import { CSFDOptions } from '../types';
import { addProtocol, getColor, parseISO8601Duration, parseIdFromUrl } from './global.helper';
import {
addProtocol,
getColor,
parseISO8601Duration,
parseIdFromUrl,
parseLastIdFromUrl
} from './global.helper';

const CREATOR_LABELS: Record<
string,
Expand Down Expand Up @@ -101,6 +109,26 @@ export const getMovieId = (el: HTMLElement): number => {
return parseIdFromUrl(url);
};

export const getSeriesAndSeasonTitle = (
el: HTMLElement
): { seriesName: string | null; seasonName: string | null } => {
const titleElement = el.querySelector('h1');
if (!titleElement) {
return { seriesName: null, seasonName: null };
}

const fullText = titleElement.innerText.trim();

// Check if there's a series part indicated by ' - '
if (fullText.includes(' - ')) {
const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());
return { seriesName, seasonName };
}
Comment on lines +122 to +126
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

split(' - ') discards segments when the title contains multiple - separators.

If a title like "Simpsonovi - Série 1 - Special" is encountered, the destructuring const [seriesName, seasonName] = ... silently drops the third part. Consider splitting only on the first occurrence:

Proposed fix
-  if (fullText.includes(' - ')) {
-    const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());
-    return { seriesName, seasonName };
-  }
+  const separatorIndex = fullText.indexOf(' - ');
+  if (separatorIndex !== -1) {
+    const seriesName = fullText.substring(0, separatorIndex).trim();
+    const seasonName = fullText.substring(separatorIndex + 3).trim();
+    return { seriesName, seasonName };
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Check if there's a series part indicated by ' - '
if (fullText.includes(' - ')) {
const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());
return { seriesName, seasonName };
}
// Check if there's a series part indicated by ' - '
const separatorIndex = fullText.indexOf(' - ');
if (separatorIndex !== -1) {
const seriesName = fullText.substring(0, separatorIndex).trim();
const seasonName = fullText.substring(separatorIndex + 3).trim();
return { seriesName, seasonName };
}
🤖 Prompt for AI Agents
In `@src/helpers/movie.helper.ts` around lines 119 - 123, The current logic uses
fullText.split(' - ') and destructures into const [seriesName, seasonName],
which drops any subsequent segments (e.g., "Simpsonovi - Série 1 - Special");
change to split only on the first occurrence by finding the first indexOf(' - ')
and slicing: compute const idx = fullText.indexOf(' - '), then set seriesName =
fullText.slice(0, idx).trim() and seasonName = fullText.slice(idx + 3).trim();
update the block where fullText is handled and keep the same returned object
shape ({ seriesName, seasonName }) so additional ' - ' parts are preserved in
seasonName.


// If no series part found, return just the name
return { seriesName: fullText, seasonName: null };
};

export const getMovieTitle = (el: HTMLElement): string => {
return el.querySelector('h1').innerText.split(`(`)[0].trim();
};
Expand Down Expand Up @@ -321,6 +349,91 @@ export const getMovieCreators = (el: HTMLElement, options?: CSFDOptions): CSFDCr
return creators;
};

export const getSeasonsOrEpisodes = (
el: HTMLElement,
serie?: { id: number; title: string }
): CSFDSeason[] | null => {
const childrenList = el.querySelector('.film-episodes-list');
if (!childrenList) return null;

const childrenNodes = childrenList.querySelectorAll('.film-title');
if (!childrenNodes?.length) return [];

return childrenNodes.map((season) => {
const nameContainer = season.querySelector('.film-title-name');
const infoContainer = season.querySelector('.info');

return {
id: parseLastIdFromUrl(nameContainer?.getAttribute('href') || ''),
name: nameContainer?.textContent?.trim() || null,
url: nameContainer?.getAttribute('href') || null,
info: infoContainer?.textContent?.replace(/[{()}]/g, '').trim() || null
};
});
};

export const getEpisodeCode = (el: HTMLElement): string | null => {
const filmHeaderName = el.querySelector('.film-header-name h1');
if (!filmHeaderName) return null;

const text = filmHeaderName.textContent?.trim() || '';
const match = text.match(/\(([^)]+)\)/);
const code = match ? match[1] : null;

return code;
};

export const detectSeasonOrEpisodeListType = (el: HTMLElement) => {
const headerText = el.querySelector('.box-header h3')?.innerText.trim() ?? '';

if (headerText.includes('Série')) return 'seasons';
if (headerText.startsWith('Epizody')) return 'episodes';
return null;
};
Comment on lines +386 to +392
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the API supports language options that would change page content
rg -n "language" src/vars.ts -A 3
echo "---"
rg -n "language" src/services/movie.service.ts -B 1 -A 3

Repository: bartholomej/node-csfd-api

Length of output: 2958


🏁 Script executed:

#!/bin/bash
# Find CSFDLanguage type definition
rg -n "CSFDLanguage" src/ -B 2 -A 5 | head -100
echo "---"
# Search for localization or translation constants
rg -n "Série|Epizody" src/ -B 2 -A 2
echo "---"
# Find where detectSeasonOrEpisodeListType is called
rg -n "detectSeasonOrEpisodeListType" src/ -B 2 -A 2

Repository: bartholomej/node-csfd-api

Length of output: 4053


🏁 Script executed:

#!/bin/bash
# Check the getMovie function context
rg -n "export class MovieScraper\|getMovie\|detectSeasonOrEpisodeListType" src/services/movie.service.ts -B 2 -A 10 | head -150
echo "---"
# Check localization helpers
rg -n "getLocalizedCreatorLabel" src/helpers/ -B 2 -A 10 | head -80

Repository: bartholomej/node-csfd-api

Length of output: 754


🏁 Script executed:

#!/bin/bash
# Check the getMovie function signature and full context
sed -n '30,80p' src/services/movie.service.ts

Repository: bartholomej/node-csfd-api

Length of output: 2053


detectSeasonOrEpisodeListType is hardcoded to Czech header text.

The strings 'Série' and 'Epizody' are Czech-only. The API supports three languages ('cs', 'en', 'sk') and receives a language parameter in the options, but detectSeasonOrEpisodeListType does not receive or use it. When called with language: 'en' or language: 'sk', the page content would be in that language, causing detection to fail and silently return null, omitting seasons and episodes data.

Either add language-aware detection (following the pattern of getLocalizedCreatorLabel which already handles localization) or document this as a Czech-only limitation.

🤖 Prompt for AI Agents
In `@src/helpers/movie.helper.ts` around lines 322 - 328,
detectSeasonOrEpisodeListType currently matches Czech-only header strings
('Série' / 'Epizody') so it will fail for 'en'/'sk'; change it to be
language-aware by adding a language parameter (e.g.,
detectSeasonOrEpisodeListType(el: HTMLElement, language: string)) and use a
small localization map or reuse the project's localization helper pattern to map
each language to the expected "seasons" and "episodes" header texts, then test
headerText against the localized variants; update all call sites to pass
options.language so seasons/episodes are detected for cs|en|sk.


export const getSeasonOrEpisodeParent = (
el: HTMLElement,
serie?: { id: number; name: string }
): CSFDParent | null => {
// Try h2 first (for episodes), then h1 (for seasons)
let parents = el.querySelectorAll('.film-header h2 a');
if (parents.length === 0) {
parents = el.querySelectorAll('.film-header h1 a');
}

if (parents.length === 0) {
if (!serie) return null;
return { series: serie, season: null };
}

const [parentSeries, parentSeason] = parents;

const seriesId = parseIdFromUrl(parentSeries?.getAttribute('href'));
const seasonId = parseLastIdFromUrl(parentSeason?.getAttribute('href') || '');
const seriesName = parentSeries?.textContent?.trim() || null;
const seasonName = parentSeason?.textContent?.trim() || null;

const series = seriesId && seriesName ? { id: seriesId, name: seriesName } : null;
const season = seasonId && seasonName ? { id: seasonId, name: seasonName } : null;

if (!series && !season) return null;

return { series, season };
};

export const getMovieGroup = (
el: HTMLElement,
group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak
): CSFDMovieCreator[] => {
const creators = el.querySelectorAll('.creators h4');
const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];
if (element?.parentNode) {
return parseMoviePeople(element.parentNode as HTMLElement);
} else {
return [];
}
};

export const getMovieType = (el: HTMLElement): string => {
const type = el.querySelector('.film-header-name .type');
return type?.innerText?.replace(/[{()}]/g, '') || 'film';
Expand Down
26 changes: 21 additions & 5 deletions src/services/movie.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { CSFDFilmTypes } from '../dto/global';
import { CSFDMovie, MovieJsonLd } from '../dto/movie';
import { fetchPage } from '../fetchers';
import {
detectSeasonOrEpisodeListType,
getEpisodeCode,
getMovieBoxMovies,
getMovieColorRating,
getMovieCreators,
Expand All @@ -21,7 +23,10 @@ import {
getMovieTrivia,
getMovieType,
getMovieVods,
getMovieYear
getMovieYear,
getSeasonOrEpisodeParent,
getSeasonsOrEpisodes,
getSeriesAndSeasonTitle
} from '../helpers/movie.helper';
import { CSFDOptions } from '../types';
import { movieUrl } from '../vars';
Expand Down Expand Up @@ -57,15 +62,21 @@ export class MovieScraper {
pageClasses: string[],
jsonLd: MovieJsonLd | null,
options: CSFDOptions
): CSFDMovie {
) {
const type = getMovieType(el) as CSFDFilmTypes;
const { seriesName = null, seasonName = null } =
type === 'série' ? getSeriesAndSeasonTitle(el) : {};
const seasonOrEpisodeListType = detectSeasonOrEpisodeListType(el);

const title = type === 'série' && seriesName ? seriesName : getMovieTitle(el);
return {
id: movieId,
title: getMovieTitle(el),
title,
year: getMovieYear(jsonLd),
duration: getMovieDuration(jsonLd, el),
descriptions: getMovieDescriptions(el),
genres: getMovieGenres(el),
type: getMovieType(el) as CSFDFilmTypes,
type,
url: movieUrl(movieId, { language: options?.language }),
origins: getMovieOrigins(el),
colorRating: getMovieColorRating(pageClasses),
Expand All @@ -80,7 +91,12 @@ export class MovieScraper {
tags: getMovieTags(asideEl),
premieres: getMoviePremieres(asideEl),
related: getMovieBoxMovies(asideEl, 'Související'),
similar: getMovieBoxMovies(asideEl, 'Podobné')
similar: getMovieBoxMovies(asideEl, 'Podobné'),
seasons: seasonOrEpisodeListType === 'seasons' ? getSeasonsOrEpisodes(el) : null,
episodes: seasonOrEpisodeListType === 'episodes' ? getSeasonsOrEpisodes(el) : null,
Comment on lines +95 to +96
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -nA15 "export const getSeasonsOrEpisodes" src/helpers/movie.helper.ts

Repository: bartholomej/node-csfd-api

Length of output: 721


🏁 Script executed:

rg -nA40 "export const getSeasonsOrEpisodes" src/helpers/movie.helper.ts

Repository: bartholomej/node-csfd-api

Length of output: 1641


getSeasonsOrEpisodes does not filter incomplete nodes or normalize URLs.

The helper function at lines 288-309 in src/helpers/movie.helper.ts maps all childrenNodes without filtering incomplete entries. Nodes with missing id, name, or url are included in the result, with null values returned for absent properties. Additionally, URLs are returned as raw href attributes without normalization—the https://www.csfd.cz prefix is not prepended, so consumers will receive relative URLs (e.g., /film/...). Add filtering logic to exclude incomplete nodes and normalize URLs to absolute paths.

🤖 Prompt for AI Agents
In src/services/movie.service.ts around lines 100-101 (root cause in
src/helpers/movie.helper.ts lines 288-309): the getSeasonsOrEpisodes mapper
currently returns entries with missing id/name/url and leaves relative hrefs
unnormalized. Update getSeasonsOrEpisodes to first filter out any child nodes
that lack a non-empty id, name, or href; then map remaining nodes to objects
with normalized absolute URLs (if href starts with '/' prepend
"https://www.csfd.cz", otherwise keep as-is). Ensure the function returns only
fully populated entries (omit null/undefined properties) and update the return
type if necessary so callers receive only valid season/episode objects with
absolute urls.

parent: type === 'seriál' ? null : getSeasonOrEpisodeParent(el, { id: movieId, name: title }),
episodeCode: type === 'epizoda' ? getEpisodeCode(el) : null,
seasonName
};
Comment on lines 94 to 100
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Parent/seasons/episodes population: verify nullability contract and absolute URLs.

  • After adjusting CSFDParent to allow nullable members, this assignment is fine.
  • Ensure getSeasonsOrEpisodes returns items with non-null id/name/url and absolute URLs to avoid downstream consumers breaking.

To verify field presence:


🏁 Script executed:

#!/bin/bash
rg -nC2 "seasons:|episodes:|parent:|episodeCode:|seasonName" src --type=ts

Length of output: 3403



---

🏁 Script executed:

```shell
#!/bin/bash
# Find CSFDParent type definition and getSeasonsOrEpisodes return type
rg -nC5 "type CSFDParent|interface CSFDParent|getSeasonsOrEpisodes.*return|export.*getSeasonsOrEpisodes" src --type=ts

Length of output: 1168


🏁 Script executed:

#!/bin/bash
# Find CSFDSeason type definition and related types
rg -nC3 "type CSFDSeason|interface CSFDSeason" src --type=ts

Length of output: 279


🏁 Script executed:

#!/bin/bash
# Search for getSeasonorEpisodeParent implementation details
rg -nC8 "getSeasonorEpisodeParent\s*=" src --type=ts

Length of output: 1162


🏁 Script executed:

#!/bin/bash
# Search for getSeasonsOrEpisodes implementation
rg -nC8 "getSeasonsOrEpisodes\s*=" src --type=ts

Length of output: 1169



---

🏁 Script executed:

```shell
#!/bin/bash
# Get complete getSeasonorEpisodeParent implementation
rg -nA20 "export const getSeasonorEpisodeParent" src/helpers/movie.helper.ts

Length of output: 934


🏁 Script executed:

#!/bin/bash
# Get complete getSeasonsOrEpisodes implementation including URL handling
rg -nA25 "export const getSeasonsOrEpisodes" src/helpers/movie.helper.ts

Length of output: 1166


🏁 Script executed:

#!/bin/bash
# Check for any recent changes to CSFDParent or related code
cat src/dto/movie.ts | head -40

Length of output: 919


Critical type contract violations in parent/seasons/episodes population require fixes.

The original review comment identified valid concerns that remain unaddressed:

  1. CSFDParent type contract violated: The getSeasonorEpisodeParent function (line 243) returns { series: serie, season: null }, and line 258 can return objects where both season and series are null, but the CSFDParent interface requires both as non-nullable { id: number; name: string; } objects.

  2. getSeasonsOrEpisodes returns nullable fields: The function (lines 213-214) assigns null to name and url fields via the || null fallback, violating the CSFDSeason interface contract which requires both fields as non-nullable strings.

  3. URLs are relative, not absolute: Line 214 returns getAttribute('href') which produces relative URLs (e.g., /film/...), not absolute URLs (e.g., https://www.csfd.cz/film/...), causing downstream consumers to fail.

Requires action:

  • Make CSFDParent.season and CSFDParent.series optional/nullable in the type definition
  • Ensure getSeasonsOrEpisodes provides non-null name/url or handle null cases
  • Prepend https://www.csfd.cz to relative URLs in getSeasonsOrEpisodes and getSeasonorEpisodeParent
🤖 Prompt for AI Agents
In src/services/movie.service.ts around lines 99-105, the
parent/seasons/episodes population violates the TypeScript contracts and returns
relative/nullable fields; update the CSFDParent type so season and series are
optional or nullable, modify getSeasonsOrEpisodes to never return null for
name/url (e.g., coerce empty strings or filter out items with missing values)
and ensure you build absolute URLs by prepending "https://www.csfd.cz" to any
relative href returned by getAttribute('href'), and update
getSeasonorEpisodeParent to return values that match the updated CSFDParent
shape (use nullable/optional fields and construct absolute URLs for any hrefs it
returns).

}
}
107 changes: 92 additions & 15 deletions tests/fetchers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,25 +159,102 @@ describe('Live: Movie page. Fetch `10135-forrest-gump`', () => {
});
});

describe('Live: Tv series', () => {
let movie: CSFDMovie = {} as CSFDMovie;
beforeAll(async () => {
movie = await csfd.movie(71924);
});
test('Year', () => {
expect(movie.year).toEqual<number>(1994);
describe('Live: Series Patterns', () => {
// Pattern 1: Series with Seasons (The Simpsons)
describe('Series with Seasons (The Simpsons)', () => {
let movie: CSFDMovie;
beforeAll(async () => {
movie = await csfd.movie(72489);
});
test('Type and Title', () => {
expect(movie.type).toEqual<CSFDFilmTypes>('seriál');
expect(movie.title).toEqual('Simpsonovi');
});
test('Seasons', () => {
expect(movie.seasons).toBeDefined();
expect(movie.seasons!.length).toBeGreaterThan(20);
});
Comment on lines +173 to +176
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

toBeDefined() does not guard against null — prefer not.toBeNull() before non-null assertion.

expect(movie.seasons).toBeDefined() passes when seasons is null, so movie.seasons!.length on Line 175 would throw a TypeError rather than a clean assertion failure. The same pattern appears on Line 199 for episodes. The mock-based tests in series.test.ts (Lines 59–61) correctly use both toBeDefined() and not.toBeNull() — align this file.

Proposed fix
     test('Seasons', () => {
       expect(movie.seasons).toBeDefined();
+      expect(movie.seasons).not.toBeNull();
       expect(movie.seasons!.length).toBeGreaterThan(20);
     });

Same for Line 198:

     test('Episodes', () => {
       expect(movie.episodes).toBeDefined();
+      expect(movie.episodes).not.toBeNull();
       expect(movie.episodes!.length).toBeGreaterThan(0);
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('Seasons', () => {
expect(movie.seasons).toBeDefined();
expect(movie.seasons!.length).toBeGreaterThan(20);
});
test('Seasons', () => {
expect(movie.seasons).toBeDefined();
expect(movie.seasons).not.toBeNull();
expect(movie.seasons!.length).toBeGreaterThan(20);
});
🤖 Prompt for AI Agents
In `@tests/fetchers.test.ts` around lines 173 - 176, The test "Seasons" (and the
similar "episodes" assertion) uses expect(movie.seasons).toBeDefined() which
does not guard against null before using the non-null assertion
movie.seasons!.length; change the assertions to first check not.toBeNull()
(e.g., expect(movie.seasons).toBeDefined();
expect(movie.seasons).not.toBeNull();) before accessing movie.seasons!.length,
and do the same pattern for movie.episodes to avoid a TypeError and match the
pattern used in series.test.ts.

test('No Episodes on main page', () => {
expect(movie.episodes).toBeNull();
});
});
test('Type', () => {
expect(movie.type).toEqual<CSFDFilmTypes>('seriál');

// Pattern 1: Season Page (The Simpsons S01)
describe('Season Page (The Simpsons S01)', () => {
let movie: CSFDMovie;
beforeAll(async () => {
movie = await csfd.movie(474212);
});
test('Type and Title', () => {
expect(movie.type).toEqual<CSFDFilmTypes>('série');
expect(movie.title).toEqual('Simpsonovi');
expect(movie.seasonName).toEqual('Série 1');
});
test('Parent Series', () => {
expect(movie.parent?.series?.id).toEqual(72489);
expect(movie.parent?.season).toBeNull();
});
test('Episodes', () => {
expect(movie.episodes).toBeDefined();
expect(movie.episodes!.length).toBeGreaterThan(0);
});
});
test('Title', () => {
expect(movie.title).toEqual<string>('Království');

// Pattern 1: Episode Page (The Simpsons S01E08)
describe('Episode Page (The Simpsons S01E08)', () => {
let movie: CSFDMovie;
beforeAll(async () => {
movie = await csfd.movie(474220);
});
test('Type and Title', () => {
expect(movie.type).toEqual<CSFDFilmTypes>('epizoda');
expect(movie.title).toEqual('Mluvící hlava');
});
test('Episode Code', () => {
expect(movie.episodeCode).toEqual('S01E08');
});
test('Parents', () => {
expect(movie.parent?.series?.id).toEqual(72489);
expect(movie.parent?.season?.id).toEqual(474212);
});
});
test('Duration', () => {
expect(movie.duration).toBeGreaterThan(50);

// Pattern 2: Series without Seasons (The Curse)
describe('Series without Seasons (The Curse)', () => {
let movie: CSFDMovie;
beforeAll(async () => {
movie = await csfd.movie(1431651);
});
test('Type and Title', () => {
expect(movie.type).toEqual<CSFDFilmTypes>('seriál');
expect(movie.title).toEqual('The Curse');
});
test('Episodes directly', () => {
expect(movie.episodes).toBeDefined();
expect(movie.episodes!.length).toBeGreaterThan(0);
});
test('No Seasons', () => {
expect(movie.seasons).toBeNull();
});
});
test('Fetch not number', async () => {
await expect(csfd.movie('test' as any)).rejects.toThrow(Error);

// Pattern 2: Episode without Season (The Curse E01)
describe('Episode without Season (The Curse E01)', () => {
let movie: CSFDMovie;
beforeAll(async () => {
movie = await csfd.movie(1436408);
});
test('Type and Title', () => {
expect(movie.type).toEqual<CSFDFilmTypes>('epizoda');
expect(movie.title).toEqual('Kouzelná země');
});
test('Episode Code', () => {
expect(movie.episodeCode).toEqual('E01');
});
test('Parents', () => {
expect(movie.parent?.series?.id).toEqual(1431651);
expect(movie.parent?.season).toBeNull();
});
});
});

Expand Down
Loading