Skip to content

Commit 266c7e5

Browse files
committed
feat(astro): support lessons without parts or chapters
1 parent 33b0398 commit 266c7e5

33 files changed

+925
-469
lines changed

e2e/configs/lessons-in-part.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import tutorialkit from '@tutorialkit/astro';
2+
import { defineConfig } from 'astro/config';
3+
4+
export default defineConfig({
5+
devToolbar: { enabled: false },
6+
server: { port: 4332 },
7+
outDir: './dist-lessons-in-part',
8+
integrations: [tutorialkit()],
9+
srcDir: './src-custom/lessons-in-part',
10+
});

e2e/configs/lessons-in-root.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import tutorialkit from '@tutorialkit/astro';
2+
import { defineConfig } from 'astro/config';
3+
4+
export default defineConfig({
5+
devToolbar: { enabled: false },
6+
server: { port: 4331 },
7+
outDir: './dist-lessons-in-root',
8+
integrations: [tutorialkit()],
9+
srcDir: './src-custom/lessons-in-root',
10+
});

e2e/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
"preview": "astro build && astro preview",
88
"dev:override-components": "astro dev --config ./configs/override-components.ts",
99
"preview:override-components": "astro build --config ./configs/override-components.ts && astro preview --config ./configs/override-components.ts",
10+
"dev:lessons-in-root": "astro dev --config ./configs/lessons-in-root.ts",
11+
"preview:lessons-in-root": "astro build --config ./configs/lessons-in-root.ts && astro preview --config ./configs/lessons-in-root.ts",
12+
"dev:lessons-in-part": "astro dev --config ./configs/lessons-in-part.ts",
13+
"preview:lessons-in-part": "astro build --config ./configs/lessons-in-part.ts && astro preview --config ./configs/lessons-in-part.ts",
1014
"test": "playwright test",
1115
"test:ui": "pnpm run test --ui"
1216
},

e2e/playwright.config.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,59 @@
11
import { defineConfig } from '@playwright/test';
22

3+
const serverOptions = {
4+
reuseExistingServer: !process.env.CI,
5+
stdout: 'ignore',
6+
stderr: 'pipe',
7+
} as const;
8+
39
export default defineConfig({
410
projects: [
511
{
612
name: 'Default',
713
testMatch: 'test/*.test.ts',
8-
testIgnore: 'test/*.override-components.test.ts',
14+
testIgnore: [
15+
'test/*.override-components.test.ts',
16+
'test/*.lessons-in-part.test.ts',
17+
'test/*.lessons-in-root.test.ts',
18+
],
919
use: { baseURL: 'http://localhost:4329' },
1020
},
1121
{
1222
name: 'Override Components',
1323
testMatch: 'test/*.override-components.test.ts',
1424
use: { baseURL: 'http://localhost:4330' },
1525
},
26+
{
27+
name: 'Lessons in root',
28+
testMatch: 'test/*.lessons-in-root.test.ts',
29+
use: { baseURL: 'http://localhost:4331' },
30+
},
31+
{
32+
name: 'Lessons in part',
33+
testMatch: 'test/*.lessons-in-part.test.ts',
34+
use: { baseURL: 'http://localhost:4332' },
35+
},
1636
],
1737
webServer: [
1838
{
1939
command: 'pnpm preview',
2040
url: 'http://localhost:4329',
21-
reuseExistingServer: !process.env.CI,
22-
stdout: 'ignore',
23-
stderr: 'pipe',
41+
...serverOptions,
2442
},
2543
{
2644
command: 'pnpm preview:override-components',
2745
url: 'http://localhost:4330',
28-
reuseExistingServer: !process.env.CI,
29-
stdout: 'ignore',
30-
stderr: 'pipe',
46+
...serverOptions,
47+
},
48+
{
49+
command: 'pnpm preview:lessons-in-root',
50+
url: 'http://localhost:4331',
51+
...serverOptions,
52+
},
53+
{
54+
command: 'pnpm preview:lessons-in-part',
55+
url: 'http://localhost:4332',
56+
...serverOptions,
3157
},
3258
],
3359
expect: {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { contentSchema } from '@tutorialkit/types';
2+
import { defineCollection } from 'astro:content';
3+
4+
const tutorial = defineCollection({
5+
type: 'content',
6+
schema: contentSchema,
7+
});
8+
9+
export const collections = { tutorial };
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: tutorial
3+
mainCommand: ''
4+
prepareCommands: []
5+
---
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
type: lesson
3+
title: Lesson one
4+
---
5+
6+
# Lessons in part test - Lesson one
7+
8+
Lesson in part without chapter
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
type: lesson
3+
title: Lesson two
4+
---
5+
6+
# Lessons in part test - Lesson two
7+
8+
Lesson in part without chapter
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
type: part
3+
title: 'Part one'
4+
---
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/// <reference path="../../.astro/types.d.ts" />
2+
/// <reference types="@tutorialkit/astro/types" />
3+
/// <reference types="astro/client" />
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { contentSchema } from '@tutorialkit/types';
2+
import { defineCollection } from 'astro:content';
3+
4+
const tutorial = defineCollection({
5+
type: 'content',
6+
schema: contentSchema,
7+
});
8+
9+
export const collections = { tutorial };
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
type: lesson
3+
title: Lesson one
4+
---
5+
6+
# Lessons in root test - Lesson one
7+
8+
Lesson in root without part
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
type: lesson
3+
title: Lesson two
4+
---
5+
6+
# Lessons in root test - Lesson two
7+
8+
Lesson in root without part
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: tutorial
3+
mainCommand: ''
4+
prepareCommands: []
5+
---
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/// <reference path="../../.astro/types.d.ts" />
2+
/// <reference types="@tutorialkit/astro/types" />
3+
/// <reference types="astro/client" />
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('user can navigate between lessons using breadcrumbs', async ({ page }) => {
4+
await page.goto('/part-one/lesson-one');
5+
6+
await expect(page.getByRole('heading', { level: 1, name: 'Lessons in part test - Lesson one' })).toBeVisible();
7+
await expect(page.getByText('Lesson in part without chapter')).toBeVisible();
8+
9+
// navigation select can take a while to hydrate on page load, click until responsive
10+
await expect(async () => {
11+
const button = page.getByRole('button', { name: 'Part one / Lesson one' });
12+
await button.click();
13+
await expect(page.locator('[data-state="open"]', { has: button })).toBeVisible({ timeout: 50 });
14+
}).toPass();
15+
16+
await page.getByRole('navigation').getByRole('link', { name: 'Lesson two' }).click();
17+
18+
await expect(page.getByRole('heading', { level: 1, name: 'Lessons in part test - Lesson two' })).toBeVisible();
19+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('user can navigate between lessons using breadcrumbs', async ({ page }) => {
4+
await page.goto('/lesson-one');
5+
6+
await expect(page.getByRole('heading', { level: 1, name: 'Lessons in root test - Lesson one' })).toBeVisible();
7+
await expect(page.getByText('Lesson in root without part')).toBeVisible();
8+
9+
// navigation select can take a while to hydrate on page load, click until responsive
10+
await expect(async () => {
11+
const button = page.getByRole('button', { name: 'Lesson one' });
12+
await button.click();
13+
await expect(page.locator('[data-state="open"]', { has: button })).toBeVisible({ timeout: 50 });
14+
}).toPass();
15+
16+
await page.getByRole('navigation').getByRole('link', { name: 'Lesson two' }).click();
17+
18+
await expect(page.getByRole('heading', { level: 1, name: 'Lessons in root test - Lesson two' })).toBeVisible();
19+
});

packages/astro/src/default/pages/index.astro

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,13 @@ import { joinPaths } from '../utils/url';
44
55
const tutorial = await getTutorial();
66
7-
const part = tutorial.parts[tutorial.firstPartId!];
8-
const chapter = part.chapters[part.firstChapterId!];
9-
const lesson = tutorial.lessons.find((l) => l.id === chapter.firstLessonId)!;
7+
const lesson = tutorial.lessons[0];
8+
const part = lesson.part && tutorial.parts[lesson.part.id];
9+
const chapter = lesson.chapter && part?.chapters[lesson.chapter.id];
1010
11-
if (!lesson) {
12-
throw new Error(
13-
`Unable to find lesson for ${JSON.stringify(
14-
{
15-
partId: tutorial.firstPartId || null,
16-
chapterId: part.firstChapterId || null,
17-
lessonId: chapter.firstLessonId || null,
18-
},
19-
null,
20-
2,
21-
)}`,
22-
);
23-
}
11+
const slug = [part?.slug, chapter?.slug, lesson.slug].filter(Boolean).join('/');
2412
25-
const redirect = joinPaths(import.meta.env.BASE_URL, `/${part.slug}/${chapter.slug}/${lesson.slug}`);
13+
const redirect = joinPaths(import.meta.env.BASE_URL, `/${slug}`);
2614
---
2715

2816
<!doctype html>

packages/astro/src/default/utils/__snapshots__/multiple-parts.json

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,6 @@
8181
"id": "1-first",
8282
"filepath": "1-part/1-chapter/1-first/content.md",
8383
"order": 0,
84-
"part": {
85-
"id": "1-part",
86-
"title": "Basics"
87-
},
88-
"chapter": {
89-
"id": "1-chapter",
90-
"title": "The first chapter in part 1"
91-
},
9284
"Markdown": "Markdown for tutorial",
9385
"slug": "lesson-slug",
9486
"files": [
@@ -99,6 +91,14 @@
9991
"1-part-1-chapter-1-first-solution.json",
10092
[]
10193
],
94+
"part": {
95+
"id": "1-part",
96+
"title": "Basics"
97+
},
98+
"chapter": {
99+
"id": "1-chapter",
100+
"title": "The first chapter in part 1"
101+
},
102102
"next": {
103103
"title": "Welcome to TutorialKit",
104104
"href": "/part-slug/chapter-slug/lesson-slug"
@@ -116,15 +116,7 @@
116116
},
117117
"id": "1-second",
118118
"filepath": "2-part/2-chapter/1-second/content.md",
119-
"order": 0,
120-
"part": {
121-
"id": "2-part",
122-
"title": "Basics"
123-
},
124-
"chapter": {
125-
"id": "2-chapter",
126-
"title": "The first chapter in part 1"
127-
},
119+
"order": 1,
128120
"Markdown": "Markdown for tutorial",
129121
"slug": "lesson-slug",
130122
"files": [
@@ -135,6 +127,14 @@
135127
"2-part-2-chapter-1-second-solution.json",
136128
[]
137129
],
130+
"part": {
131+
"id": "2-part",
132+
"title": "Basics"
133+
},
134+
"chapter": {
135+
"id": "2-chapter",
136+
"title": "The first chapter in part 1"
137+
},
138138
"prev": {
139139
"title": "Welcome to TutorialKit",
140140
"href": "/part-slug/chapter-slug/lesson-slug"
@@ -156,15 +156,7 @@
156156
},
157157
"id": "1-third",
158158
"filepath": "3-part/3-chapter/1-third/content.md",
159-
"order": 0,
160-
"part": {
161-
"id": "3-part",
162-
"title": "Basics"
163-
},
164-
"chapter": {
165-
"id": "3-chapter",
166-
"title": "The first chapter in part 1"
167-
},
159+
"order": 2,
168160
"Markdown": "Markdown for tutorial",
169161
"slug": "lesson-slug",
170162
"files": [
@@ -175,6 +167,14 @@
175167
"3-part-3-chapter-1-third-solution.json",
176168
[]
177169
],
170+
"part": {
171+
"id": "3-part",
172+
"title": "Basics"
173+
},
174+
"chapter": {
175+
"id": "3-chapter",
176+
"title": "The first chapter in part 1"
177+
},
178178
"prev": {
179179
"title": "Welcome to TutorialKit",
180180
"href": "/part-slug/chapter-slug/lesson-slug"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"parts": {},
3+
"lessons": [
4+
{
5+
"data": {
6+
"type": "lesson",
7+
"title": "Welcome to TutorialKit",
8+
"template": "default",
9+
"i18n": {
10+
"mocked": "default localization"
11+
},
12+
"openInStackBlitz": true
13+
},
14+
"id": "1-lesson",
15+
"filepath": "1-lesson/content.md",
16+
"order": 0,
17+
"Markdown": "Markdown for tutorial",
18+
"slug": "lesson-slug",
19+
"files": [
20+
"1-lesson-files.json",
21+
[]
22+
],
23+
"solution": [
24+
"1-lesson-solution.json",
25+
[]
26+
]
27+
}
28+
]
29+
}

0 commit comments

Comments
 (0)