diff --git a/apps/librelingo-web/src/app/[sourceLanguageCode]/courses/[targetLanguageCode]/page.tsx b/apps/librelingo-web/src/app/[sourceLanguageCode]/courses/[targetLanguageCode]/page.tsx
index fea9b2f476b..e0ea6d133dd 100644
--- a/apps/librelingo-web/src/app/[sourceLanguageCode]/courses/[targetLanguageCode]/page.tsx
+++ b/apps/librelingo-web/src/app/[sourceLanguageCode]/courses/[targetLanguageCode]/page.tsx
@@ -20,5 +20,14 @@ export default async function CourseHomePage({params}: Props) {
const courseId = await getCourseId(params)
const detail = await getCourseDetail(courseId)
- return
{detail.targetLanguage.name}
+ return <>{detail.targetLanguage.name}
+
+ {detail.modules.map((module) => (
+ -
+ {module.title}
+
+ ))}
+
+
+ >
}
diff --git a/apps/librelingo-web/src/data/course.ts b/apps/librelingo-web/src/data/course.ts
index 6f8a7273bc3..03041162c36 100644
--- a/apps/librelingo-web/src/data/course.ts
+++ b/apps/librelingo-web/src/data/course.ts
@@ -87,11 +87,13 @@ export async function getCourseId(
}
export async function getCourseDetail(courseId: string) {
- const { languageName } = await getCourseMetadataByJsonPath(courseId)
+ const { languageName, modules } =
+ await getCourseMetadataByJsonPath(courseId)
return {
targetLanguage: {
name: languageName,
},
+ modules,
}
}
diff --git a/e2e-tests/course.spec.ts b/e2e-tests/course.spec.ts
index bc7f324a8ba..09df4361bce 100644
--- a/e2e-tests/course.spec.ts
+++ b/e2e-tests/course.spec.ts
@@ -7,3 +7,26 @@ test('has the correct content', async ({ page }) => {
page.getByRole('heading', { name: 'Test Language' })
).toBeVisible()
})
+
+test('has cards leading to module pages', async ({ page }) => {
+ const courseHomePagePattern = new RegExp(
+ `[^/]*/courses/[^/]+/modules/[^/]+`
+ )
+ await page.goto('/en/courses/test-1')
+
+ const cards = await page.getByRole('listitem').all()
+
+ expect(cards.length).toBeGreaterThanOrEqual(1)
+ const urls = new Set()
+
+ for (const card of cards) {
+ const button = card.getByRole('link', { name: 'Learn' })
+ const url = await button.getAttribute('href')
+
+ expect(url).toMatch(courseHomePagePattern)
+ urls.add(url)
+ }
+
+ // each course has to have a unique url
+ expect(urls.size).toBeGreaterThanOrEqual(cards.length)
+})
diff --git a/e2e-tests/home.spec.ts b/e2e-tests/home.spec.ts
index 551c219b1a1..2544c66f808 100644
--- a/e2e-tests/home.spec.ts
+++ b/e2e-tests/home.spec.ts
@@ -10,7 +10,9 @@ test('has the correct content', async ({ page }) => {
await expect(firstCard.getByRole('link', { name: 'Learn' })).toBeVisible()
})
-test('all card buttons lead to URLs matching the pattern', async ({ page }) => {
+test('has cards for each course leading to the course page', async ({
+ page,
+}) => {
const courseHomePagePattern = new RegExp(`[^/]*/courses/[^/]+`)
await page.goto('/')
diff --git a/src/librelingo_json_export/module.py b/src/librelingo_json_export/module.py
index 20d21b29f6a..d51377f0d3a 100644
--- a/src/librelingo_json_export/module.py
+++ b/src/librelingo_json_export/module.py
@@ -29,6 +29,7 @@ def get_levels(words, phrases):
return calculate_number_of_levels(len(words), len(phrases))
return {
+ "slug": slugify(module.title),
"title": module.title,
"skills": [
{
diff --git a/src/librelingo_json_export/tests/test_course_get_course_data.py b/src/librelingo_json_export/tests/test_course_get_course_data.py
index e2564f11783..b4394506f80 100644
--- a/src/librelingo_json_export/tests/test_course_get_course_data.py
+++ b/src/librelingo_json_export/tests/test_course_get_course_data.py
@@ -39,6 +39,7 @@ def test__get_course_data_return_value():
},
"modules": [
{
+ "slug": "basics",
"title": "Basics",
"skills": [
{
@@ -67,7 +68,7 @@ def test__get_course_data_return_value():
},
],
},
- {"title": "Phrases", "skills": []},
+ {"slug": "phrases", "title": "Phrases", "skills": []},
],
}
@@ -89,6 +90,7 @@ def test__get_course_data_return_value_2():
},
"modules": [
{
+ "slug": "animals",
"title": "Animals",
"skills": [
{
@@ -122,6 +124,7 @@ def test__get_course_data_return_value_with_introduction():
},
"modules": [
{
+ "slug": "animals",
"title": "Animals",
"skills": [
{