This commit is contained in:
49lf
2025-01-14 17:36:03 -05:00
parent 5ada05063b
commit f79f11fd82
2 changed files with 78 additions and 79 deletions

View File

@ -1,79 +1,72 @@
import { assertParse, initPromise, programMemoryInit } from './wasm'
import { enginelessExecutor } from '../lib/testHelpers'
// These unit tests makes web requests to a public github repository.
import path from 'node:path'
import fs from 'node:fs/promises'
import child_process from 'node:child_process'
// The purpose of these tests is to act as a first line of defense
// if something gets real screwy with our KCL ecosystem.
// THESE TESTS ONLY RUN UNDER A NODEJS ENVIRONMENT. They DO NOT
// test under our application.
const DIR_KCL_SAMPLES = 'kcl-samples'
const URL_GIT_KCL_SAMPLES = 'https://github.com/KittyCAD/kcl-samples.git'
interface KclSampleFile {
file: string
pathFromProjectDirectoryToFirstFile: string
title: string
filename: string
description: string
}
try {
await fs.rm(DIR_KCL_SAMPLES, { recursive: true })
} catch (e) {
console.log(e)
}
child_process.spawnSync('git', ['clone', URL_GIT_KCL_SAMPLES, DIR_KCL_SAMPLES])
let files = await fs.readdir(DIR_KCL_SAMPLES)
const manifestJsonStr = await fs.readFile(
path.resolve(DIR_KCL_SAMPLES, 'manifest.json'),
'utf-8'
)
const manifest = JSON.parse(manifestJsonStr)
process.chdir(DIR_KCL_SAMPLES)
beforeAll(async () => {
await initPromise
})
// Only used to actually fetch an older version of KCL code that will break in the parser.
/* eslint-disable @typescript-eslint/no-unused-vars */
async function getBrokenSampleCodeForLocalTesting() {
const result = await fetch(
'https://raw.githubusercontent.com/KittyCAD/kcl-samples/5ccd04a1773ebdbfd02684057917ce5dbe0eaab3/80-20-rail.kcl'
)
const text = await result.text()
return text
}
afterAll(async () => {
try {
process.chdir('..')
await fs.rm(DIR_KCL_SAMPLES, { recursive: true })
} catch (e) {}
})
async function getKclSampleCodeFromGithub(file: string): Promise<string> {
const result = await fetch(
`https://raw.githubusercontent.com/KittyCAD/kcl-samples/refs/heads/main/${file}/${file}.kcl`
)
const text = await result.text()
return text
}
async function getFileNamesFromManifestJSON(): Promise<KclSampleFile[]> {
const result = await fetch(
'https://raw.githubusercontent.com/KittyCAD/kcl-samples/refs/heads/main/manifest.json'
)
const json = await result.json()
json.forEach((file: KclSampleFile) => {
const filenameWithoutExtension = file.file.split('.')[0]
file.filename = filenameWithoutExtension
})
return json
}
// Value to use across all tests!
let files: KclSampleFile[] = []
describe('Test KCL Samples from public Github repository', () => {
describe('When parsing source code', () => {
// THIS RUNS ACROSS OTHER TESTS!
it('should fetch files', async () => {
files = await getFileNamesFromManifestJSON()
})
// Run through all of the files in the manifest json. This will allow us to be automatically updated
// with the latest changes in github. We won't be hard coding the filenames
files.forEach((file: KclSampleFile) => {
it(`should parse ${file.filename} without errors`, async () => {
const code = await getKclSampleCodeFromGithub(file.filename)
assertParse(code)
}, 1000)
})
})
describe('when performing enginelessExecutor', () => {
it(
'should run through all the files',
async () => {
for (let i = 0; i < files.length; i++) {
const file: KclSampleFile = files[i]
const code = await getKclSampleCodeFromGithub(file.filename)
// The tests have to be sequential because we need to change directories
// to support `import` working properly.
describe.sequential('Test KCL Samples from public Github repository', () => {
describe.sequential('when performing enginelessExecutor', () => {
manifest.forEach((file: KclSampleFile) => {
it.sequential(
`should execute ${file.title} (${file.file}) successfully`,
async () => {
const [dirProject, fileKcl] =
file.pathFromProjectDirectoryToFirstFile.split('/')
process.chdir(dirProject)
const code = await fs.readFile(fileKcl, 'utf-8')
const ast = assertParse(code)
await enginelessExecutor(ast, programMemoryInit())
}
},
files.length * 1000
)
process.chdir('..')
},
files.length * 1000
)
})
})
})

View File

@ -1,5 +1,23 @@
import { isDesktop } from 'lib/isDesktop'
// Polyfill window.electron fs functions as needed when in a nodejs context
// (INTENDED FOR VITEST SHINANGANS)
if (window?.electron === undefined) {
;(async () => {
const fs = await import('node:fs/promises')
const path = await import('node:path')
Object.assign(window, {
electron: {
readFile: fs.readFile,
stat: fs.stat,
readdir: fs.readdir,
path,
process: {},
},
})
})().catch(console.error)
}
/// FileSystemManager is a class that provides a way to read files from the local file system.
/// It assumes that you are in a project since it is solely used by the std lib
/// when executing code.
@ -19,13 +37,9 @@ class FileSystemManager {
}
async readFile(path: string): Promise<Uint8Array> {
// Using local file system only works from desktop.
if (!isDesktop()) {
return Promise.reject(
new Error(
'This function can only be called from the desktop application'
)
)
// Using local file system only works from desktop and nodejs
if (!window?.electron?.readFile) {
return Promise.reject(new Error('No polyfill found for this function'))
}
return this.join(this.dir, path).then((filePath) => {
@ -35,12 +49,8 @@ class FileSystemManager {
async exists(path: string): Promise<boolean | void> {
// Using local file system only works from desktop.
if (!isDesktop()) {
return Promise.reject(
new Error(
'This function can only be called from the desktop application'
)
)
if (!window?.electron?.stat) {
return Promise.reject(new Error('No polyfill found for this function'))
}
return this.join(this.dir, path).then(async (file) => {
@ -57,12 +67,8 @@ class FileSystemManager {
async getAllFiles(path: string): Promise<string[] | void> {
// Using local file system only works from desktop.
if (!isDesktop()) {
return Promise.reject(
new Error(
'This function can only be called from the desktop application'
)
)
if (!window?.electron?.readdir) {
return Promise.reject(new Error('No polyfill found for this function'))
}
return this.join(this.dir, path).then((filepath) => {