Guptaarnav 2024 10 28 (#4341)
* accessing toast error correctly * wrapping try-catch around fs.stat on cli arg * implemented array push * changing arg execution order for sketch arc * addressing sketchFromKclValue error for Sketches in Uservals * addressing 'update to He inside a test not wrapped in act(...' error * yarn fmt fix * implemented polygon stdlib function * changing polygon inscribed arg description in docs * addressing cargo clippy warning * Add tangential arc unavailable reason tooltip * fixing tsc errors * preventing hidden dirs from showing up as projects and prohibits renaming projects as hidden * adding unit test for desktop listProjects * showing no completions when last typed word is a number * fmt * Make clippy happy * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest) * yarn tsc fix: added missing toast import in Home.tsx * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest) * regenerating markdown docs for incoming merge from main --------- Co-authored-by: arnav <arnav@agupta.org> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
131
src/lib/desktop.test.ts
Normal file
131
src/lib/desktop.test.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest'
|
||||
import { listProjects } from './desktop'
|
||||
import { DeepPartial } from './types'
|
||||
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||
|
||||
// Mock the electron window global
|
||||
const mockElectron = {
|
||||
readdir: vi.fn(),
|
||||
path: {
|
||||
join: vi.fn(),
|
||||
basename: vi.fn(),
|
||||
dirname: vi.fn(),
|
||||
},
|
||||
stat: vi.fn(),
|
||||
statIsDirectory: vi.fn(),
|
||||
exists: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
readFile: vi.fn(),
|
||||
os: {
|
||||
isMac: false,
|
||||
isWindows: false,
|
||||
},
|
||||
process: {
|
||||
env: {},
|
||||
},
|
||||
getPath: vi.fn(),
|
||||
kittycad: vi.fn(),
|
||||
}
|
||||
|
||||
vi.stubGlobal('window', { electron: mockElectron })
|
||||
|
||||
describe('desktop utilities', () => {
|
||||
const mockConfig: DeepPartial<Configuration> = {
|
||||
settings: {
|
||||
project: {
|
||||
directory: '/test/projects',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockFileSystem: { [key: string]: string[] } = {
|
||||
'/test/projects': [
|
||||
'.hidden-project',
|
||||
'valid-project',
|
||||
'.git',
|
||||
'project-without-kcl-files',
|
||||
'another-valid-project',
|
||||
],
|
||||
'/test/projects/valid-project': ['file1.kcl', 'file2.stp'],
|
||||
'/test/projects/project-without-kcl-files': ['file3.glb'],
|
||||
'/test/projects/another-valid-project': ['file4.kcl'],
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Setup default mock implementations
|
||||
mockElectron.path.join.mockImplementation((...parts: string[]) =>
|
||||
parts.join('/')
|
||||
)
|
||||
mockElectron.path.basename.mockImplementation((path: string) =>
|
||||
path.split('/').pop()
|
||||
)
|
||||
mockElectron.path.dirname.mockImplementation((path: string) =>
|
||||
path.split('/').slice(0, -1).join('/')
|
||||
)
|
||||
|
||||
// Mock readdir to return the entries for the given path
|
||||
mockElectron.readdir.mockImplementation(async (path: string) => {
|
||||
return mockFileSystem[path] || []
|
||||
})
|
||||
|
||||
// Mock statIsDirectory to return true if the path exists in mockFileSystem
|
||||
mockElectron.statIsDirectory.mockImplementation(async (path: string) => {
|
||||
return path in mockFileSystem
|
||||
})
|
||||
|
||||
// Mock stat to always resolve with dummy metadata
|
||||
mockElectron.stat.mockResolvedValue({
|
||||
mtimeMs: 123,
|
||||
atimeMs: 456,
|
||||
ctimeMs: 789,
|
||||
size: 100,
|
||||
mode: 0o666,
|
||||
})
|
||||
|
||||
mockElectron.exists.mockResolvedValue(true)
|
||||
mockElectron.readFile.mockResolvedValue('')
|
||||
mockElectron.writeFile.mockResolvedValue(undefined)
|
||||
mockElectron.getPath.mockResolvedValue('/appData')
|
||||
mockElectron.kittycad.mockResolvedValue({})
|
||||
})
|
||||
|
||||
describe('listProjects', () => {
|
||||
it('does not list .git directories', async () => {
|
||||
const projects = await listProjects(mockConfig)
|
||||
expect(projects.map((p) => p.name)).not.toContain('.git')
|
||||
})
|
||||
it('lists projects excluding hidden and without .kcl files', async () => {
|
||||
const projects = await listProjects(mockConfig)
|
||||
|
||||
// Verify only non-dot projects with .kcl files were included
|
||||
expect(projects.map((p) => p.name)).toEqual([
|
||||
'valid-project',
|
||||
'another-valid-project',
|
||||
])
|
||||
|
||||
// Verify we didn't try to get project info for dot directories
|
||||
expect(mockElectron.stat).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('/.hidden-project')
|
||||
)
|
||||
expect(mockElectron.stat).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('/.git')
|
||||
)
|
||||
|
||||
// Verify that projects without .kcl files are not included
|
||||
expect(projects.map((p) => p.name)).not.toContain(
|
||||
'project-without-kcl-files'
|
||||
)
|
||||
})
|
||||
|
||||
it('handles empty project directory', async () => {
|
||||
// Adjust mockFileSystem to simulate empty directory
|
||||
mockFileSystem['/test/projects'] = []
|
||||
|
||||
const projects = await listProjects(mockConfig)
|
||||
|
||||
expect(projects).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -139,6 +139,11 @@ export async function listProjects(
|
||||
|
||||
const entries = await window.electron.readdir(projectDir)
|
||||
for (let entry of entries) {
|
||||
// Skip directories that start with a dot
|
||||
if (entry.startsWith('.')) {
|
||||
continue
|
||||
}
|
||||
|
||||
const projectPath = window.electron.path.join(projectDir, entry)
|
||||
// if it's not a directory ignore.
|
||||
const isDirectory = await window.electron.statIsDirectory(projectPath)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs/promises'
|
||||
import { Stats } from 'fs'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import { PROJECT_ENTRYPOINT } from './constants'
|
||||
|
||||
@ -43,8 +44,16 @@ export default async function getCurrentProjectFile(
|
||||
? sourcePath
|
||||
: path.join(process.cwd(), sourcePath)
|
||||
|
||||
let stats: Stats
|
||||
try {
|
||||
stats = await fs.stat(sourcePath)
|
||||
} catch (error) {
|
||||
return new Error(
|
||||
`Unable to access the path: ${sourcePath}. Error: ${error}`
|
||||
)
|
||||
}
|
||||
|
||||
// If the path is a directory, let's assume it is a project directory.
|
||||
const stats = await fs.stat(sourcePath)
|
||||
if (stats.isDirectory()) {
|
||||
// Walk the directory and look for a kcl file.
|
||||
const files = await fs.readdir(sourcePath)
|
||||
|
||||
@ -39,6 +39,9 @@ export type ToolbarItem = {
|
||||
description: string
|
||||
links: { label: string; url: string }[]
|
||||
isActive?: (state: StateFrom<typeof modelingMachine>) => boolean
|
||||
disabledReason?:
|
||||
| string
|
||||
| ((state: StateFrom<typeof modelingMachine>) => string | undefined)
|
||||
}
|
||||
|
||||
export type ToolbarItemResolved = Omit<
|
||||
@ -349,6 +352,11 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
(!isEditingExistingSketch(state.context) &&
|
||||
!state.matches({ Sketch: 'Tangential arc to' })) ||
|
||||
pipeHasCircle(state.context),
|
||||
disabledReason: (state) =>
|
||||
!isEditingExistingSketch(state.context) &&
|
||||
!state.matches({ Sketch: 'Tangential arc to' })
|
||||
? "Cannot start a tangential arc because there's no previous line to be tangential to. Try drawing a line first or selecting an existing sketch to edit."
|
||||
: undefined,
|
||||
title: 'Tangential Arc',
|
||||
hotkey: (state) =>
|
||||
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
|
||||
|
||||
Reference in New Issue
Block a user