diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6becfa674..c00ef21ba 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + workflow_dispatch: jobs: test: diff --git a/packages/astro/src/default/utils/content.ts b/packages/astro/src/default/utils/content.ts index f70743fb2..68d2cb55e 100644 --- a/packages/astro/src/default/utils/content.ts +++ b/packages/astro/src/default/utils/content.ts @@ -1,23 +1,12 @@ -import type { - ChapterSchema, - FilesRefList, - Lesson, - LessonSchema, - PartSchema, - Tutorial, - TutorialSchema, -} from '@tutorialkit/types'; -import { folderPathToFilesRef, interpolateString } from '@tutorialkit/types'; +import type { ChapterSchema, Lesson, LessonSchema, PartSchema, Tutorial, TutorialSchema } from '@tutorialkit/types'; +import { interpolateString } from '@tutorialkit/types'; import { getCollection } from 'astro:content'; -import glob from 'fast-glob'; import path from 'node:path'; -import { IGNORED_FILES } from './constants'; import { DEFAULT_LOCALIZATION } from './content/default-localization'; import { squash } from './content/squash.js'; import { logger } from './logger'; import { joinPaths } from './url'; - -const CONTENT_DIR = path.join(process.cwd(), 'src/content/tutorial'); +import { getFilesRefList } from './content/files-ref'; export async function getTutorial(): Promise { const collection = sortCollection(await getCollection('tutorial')); @@ -331,24 +320,6 @@ function getSlug(entry: CollectionEntryTutorial) { return slug; } -async function getFilesRefList(pathToFolder: string): Promise { - const root = path.join(CONTENT_DIR, pathToFolder); - - const filePaths = ( - await glob(`${glob.convertPathToPattern(root)}/**/*`, { - onlyFiles: true, - ignore: IGNORED_FILES, - dot: true, - }) - ).map((filePath) => `/${path.relative(root, filePath)}`); - - filePaths.sort(); - - const filesRef = folderPathToFilesRef(pathToFolder); - - return [filesRef, filePaths]; -} - interface CollectionEntryTutorial { id: string; slug: string; diff --git a/packages/astro/src/default/utils/content/files-ref.spec.ts b/packages/astro/src/default/utils/content/files-ref.spec.ts new file mode 100644 index 000000000..5aa5c70c5 --- /dev/null +++ b/packages/astro/src/default/utils/content/files-ref.spec.ts @@ -0,0 +1,17 @@ +import { expect, test } from 'vitest'; + +import { getFilesRefList } from './files-ref'; + +test('getFilesRefList returns files', async () => { + const files = await getFilesRefList('test/fixtures/files', ''); + + expect(files).toMatchInlineSnapshot(` + [ + "test-fixtures-files.json", + [ + "/first.js", + "/nested/directory/second.ts", + ], + ] + `); +}); diff --git a/packages/astro/src/default/utils/content/files-ref.ts b/packages/astro/src/default/utils/content/files-ref.ts new file mode 100644 index 000000000..553c3a127 --- /dev/null +++ b/packages/astro/src/default/utils/content/files-ref.ts @@ -0,0 +1,25 @@ +import type { FilesRefList } from '@tutorialkit/types'; +import { folderPathToFilesRef } from '@tutorialkit/types'; +import glob from 'fast-glob'; +import path from 'node:path'; +import { IGNORED_FILES } from '../constants'; + +const CONTENT_DIR = path.join(process.cwd(), 'src/content/tutorial'); + +export async function getFilesRefList(pathToFolder: string, base = CONTENT_DIR): Promise { + const root = path.join(base, pathToFolder); + + const filePaths = ( + await glob(`${glob.convertPathToPattern(root)}/**/*`, { + onlyFiles: true, + ignore: IGNORED_FILES, + dot: true, + }) + ).map((filePath) => `/${path.relative(root, filePath).replaceAll(path.sep, '/')}`); + + filePaths.sort(); + + const filesRef = folderPathToFilesRef(pathToFolder); + + return [filesRef, filePaths]; +} diff --git a/packages/astro/test/fixtures/files/first.js b/packages/astro/test/fixtures/files/first.js new file mode 100644 index 000000000..424f47511 --- /dev/null +++ b/packages/astro/test/fixtures/files/first.js @@ -0,0 +1 @@ +export default 'Example JS file'; diff --git a/packages/astro/test/fixtures/files/nested/directory/second.ts b/packages/astro/test/fixtures/files/nested/directory/second.ts new file mode 100644 index 000000000..c70e3c00c --- /dev/null +++ b/packages/astro/test/fixtures/files/nested/directory/second.ts @@ -0,0 +1 @@ +export default 'Example TS file'; diff --git a/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap b/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap index ee67bba96..381191655 100644 --- a/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap +++ b/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap @@ -94,6 +94,58 @@ exports[`create a project 1`] = ` ] `; +exports[`create and build a project > built project file references 1`] = ` +{ + "1-basics-1-introduction-1-welcome-files.json": [ + "/src/index.js", + "/src/test/bar.js", + ], + "1-basics-1-introduction-1-welcome-solution.json": [ + "/src/index.js", + ], + "1-basics-1-introduction-2-foo-files.json": [ + "/bar/styles.css", + "/src/index.html", + "/src/unicorn.js", + "/src/windows_xp.png", + ], + "1-basics-1-introduction-2-foo-solution.json": [ + "/src/index.html", + ], + "1-basics-1-introduction-3-bar-files.json": [ + "/src/index.html", + ], + "template-default.json": [ + "/package-lock.json", + "/package.json", + "/src/index.js", + ], + "template-vite-app-2.json": [ + "/.gitignore", + "/counter.js", + "/foo.txt", + "/index.html", + "/javascript.svg", + "/main.js", + "/package-lock.json", + "/package.json", + "/public/vite.svg", + "/style.css", + ], + "template-vite-app.json": [ + "/.gitignore", + "/counter.js", + "/index.html", + "/javascript.svg", + "/main.js", + "/package-lock.json", + "/package.json", + "/public/vite.svg", + "/style.css", + ], +} +`; + exports[`create and build a project 1`] = ` [ "1-basics", @@ -253,6 +305,7 @@ exports[`create and eject a project 1`] = ` "src/utils/content", "src/utils/content.ts", "src/utils/content/default-localization.ts", + "src/utils/content/files-ref.ts", "src/utils/content/squash.ts", "src/utils/logger.ts", "src/utils/nav.ts", diff --git a/packages/cli/tests/create-tutorial.test.ts b/packages/cli/tests/create-tutorial.test.ts index 0809e4e8a..dce8f256a 100644 --- a/packages/cli/tests/create-tutorial.test.ts +++ b/packages/cli/tests/create-tutorial.test.ts @@ -1,5 +1,6 @@ import { execa } from 'execa'; import fs from 'node:fs/promises'; +import { readFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { afterAll, beforeAll, expect, test } from 'vitest'; @@ -67,6 +68,20 @@ test('create and build a project', async (context) => { const distFiles = await fs.readdir(path.join(dest, 'dist'), { recursive: true }); expect(distFiles.map(normaliseSlash).sort()).toMatchSnapshot(); + + // create snapshot of lesson, solution and template file reference JSONs + const lessonJsons = distFiles.filter((file) => file.endsWith('-files.json')); + const solutionJsons = distFiles.filter((file) => file.endsWith('-solution.json')); + const templateJsons = distFiles.filter((file) => file.startsWith('template-') && file.endsWith('.json')); + + const contents = [...lessonJsons, ...solutionJsons, ...templateJsons].reduce((jsons, current) => { + const fileJson = JSON.parse(readFileSync(path.join(dest, 'dist', current), 'utf8')); + const filenames = Object.keys(fileJson); + + return { ...jsons, [current]: filenames }; + }, {}); + + expect(contents).toMatchSnapshot('built project file references'); }); test('create and eject a project', async (context) => {