Franknoirot/bugfix directory picker (#2025)

* Fix project directory setting input

* Remove unused imports

* Almost working Tauri test

* Finish Tauri e2e test

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* fmt

* Try a different Webriver selector

* Update themeColor component to use new updateValue API

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Rerun CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Frank Noirot
2024-04-05 01:48:12 -04:00
committed by GitHub
parent d85781ef99
commit 47b5fa1459
9 changed files with 66 additions and 33 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,7 +1,10 @@
import { browser, $, expect } from '@wdio/globals' import { browser, $, expect } from '@wdio/globals'
import fs from 'fs/promises' import fs from 'fs/promises'
const defaultDir = `${process.env.HOME}/Documents/zoo-modeling-app-projects` const documentsDir = `${process.env.HOME}/Documents`
const userSettingsFile = `${process.env.HOME}/.config/dev.zoo.modeling-app/user.toml`
const defaultProjectDir = `${documentsDir}/zoo-modeling-app-projects`
const newProjectDir = `${documentsDir}/a-different-directory`
const userCodeDir = '/tmp/kittycad_user_code' const userCodeDir = '/tmp/kittycad_user_code'
async function click(element: WebdriverIO.Element): Promise<void> { async function click(element: WebdriverIO.Element): Promise<void> {
@ -10,12 +13,25 @@ async function click(element: WebdriverIO.Element): Promise<void> {
await browser.execute('arguments[0].click();', element) await browser.execute('arguments[0].click();', element)
} }
/* Shoutout to @Sheap on Github for a great workaround utility:
* https://github.com/tauri-apps/tauri/issues/6541#issue-1638944060
*/
async function setDatasetValue(
field: WebdriverIO.Element,
property: string,
value: string
) {
await browser.execute(`arguments[0].dataset.${property} = "${value}"`, field)
}
describe('ZMA (Tauri, Linux)', () => { describe('ZMA (Tauri, Linux)', () => {
it('opens the auth page and signs in', async () => { it('opens the auth page and signs in', async () => {
// Clean up filesystem from previous tests // Clean up filesystem from previous tests
await new Promise((resolve) => setTimeout(resolve, 100)) await new Promise((resolve) => setTimeout(resolve, 100))
await fs.rm(defaultDir, { force: true, recursive: true }) await fs.rm(defaultProjectDir, { force: true, recursive: true })
await fs.rm(userCodeDir, { force: true }) await fs.rm(userCodeDir, { force: true })
await fs.rm(userSettingsFile, { force: true })
await fs.mkdir(newProjectDir, { recursive: true })
const signInButton = await $('[data-testid="sign-in-button"]') const signInButton = await $('[data-testid="sign-in-button"]')
expect(await signInButton.getText()).toEqual('Sign in') expect(await signInButton.getText()).toEqual('Sign in')
@ -65,8 +81,20 @@ describe('ZMA (Tauri, Linux)', () => {
const settingsButton = await $('[data-testid="settings-button"]') const settingsButton = await $('[data-testid="settings-button"]')
await click(settingsButton) await click(settingsButton)
const defaultDirInput = await $('[data-testid="default-directory-input"]') const projectDirInput = await $('[data-testid="project-directory-input"]')
expect(await defaultDirInput.getValue()).toEqual(defaultDir) expect(await projectDirInput.getValue()).toEqual(defaultProjectDir)
/*
* We've set up the project directory input (in initialSettings.tsx)
* to be able to skip the folder selection dialog if data-testValue
* has a value, allowing us to test the input otherwise works.
*/
await setDatasetValue(projectDirInput, 'testValue', newProjectDir)
const projectDirButton = await $('[data-testid="project-directory-button"]')
await click(projectDirButton)
await new Promise((resolve) => setTimeout(resolve, 500))
// This line is broken. I need a different way to grab the toast
await expect(await $('div*=Set project directory to')).toBeDisplayed()
const nameInput = await $('[data-testid="projects-defaultProjectName"]') const nameInput = await $('[data-testid="projects-defaultProjectName"]')
expect(await nameInput.getValue()).toEqual('project-$nnn') expect(await nameInput.getValue()).toEqual('project-$nnn')

View File

@ -135,6 +135,7 @@ export const SettingsAuthProviderBase = ({
: '') : '')
toast.success(message, { toast.success(message, {
duration: message.split(' ').length * 100 + 1500, duration: message.split(' ').length * 100 + 1500,
id: `${event.type}.success`,
}) })
}, },
'Execute AST': () => kclManager.executeAst(), 'Execute AST': () => kclManager.executeAst(),

View File

@ -13,7 +13,7 @@ import {
cameraSystems, cameraSystems,
} from 'lib/cameraControls' } from 'lib/cameraControls'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { CSSProperties, useRef } from 'react' import { useRef } from 'react'
import { open } from '@tauri-apps/api/dialog' import { open } from '@tauri-apps/api/dialog'
import { CustomIcon } from 'components/CustomIcon' import { CustomIcon } from 'components/CustomIcon'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
@ -136,11 +136,11 @@ export function createSettings() {
defaultValue: '264.5', defaultValue: '264.5',
description: 'The hue of the primary theme color for the app', description: 'The hue of the primary theme color for the app',
validate: (v) => Number(v) >= 0 && Number(v) < 360, validate: (v) => Number(v) >= 0 && Number(v) < 360,
Component: ({ value, onChange }) => ( Component: ({ value, updateValue }) => (
<div className="flex item-center gap-2 px-2"> <div className="flex item-center gap-2 px-2">
<input <input
type="range" type="range"
onChange={onChange} onChange={(e) => updateValue(e.currentTarget.value)}
value={value} value={value}
min={0} min={0}
max={259} max={259}
@ -176,35 +176,41 @@ export function createSettings() {
hideOnLevel: 'project', hideOnLevel: 'project',
hideOnPlatform: 'web', hideOnPlatform: 'web',
validate: (v) => typeof v === 'string' && (v.length > 0 || !isTauri()), validate: (v) => typeof v === 'string' && (v.length > 0 || !isTauri()),
Component: ({ value, onChange }) => { Component: ({ value, updateValue }) => {
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
return ( return (
<div className="flex gap-4 p-1 border rounded-sm border-chalkboard-30"> <div className="flex gap-4 p-1 border rounded-sm border-chalkboard-30">
<input <input
className="flex-grow text-xs px-2 bg-transparent" className="flex-grow text-xs px-2 bg-transparent"
value={value} value={value}
onBlur={onChange}
disabled disabled
data-testid="default-directory-input" data-testid="project-directory-input"
ref={inputRef}
/> />
<button <button
onClick={async () => { onClick={async () => {
const newValue = await open({ // In Tauri end-to-end tests we can't control the file picker,
directory: true, // so we seed the new directory value in the element's dataset
recursive: true, const newValue =
defaultPath: value, inputRef.current && inputRef.current.dataset.testValue
title: 'Choose a new default directory', ? inputRef.current.dataset.testValue
}) : await open({
directory: true,
recursive: true,
defaultPath: value,
title: 'Choose a new project directory',
})
if ( if (
inputRef.current &&
newValue && newValue &&
newValue !== null && newValue !== null &&
newValue !== value &&
!Array.isArray(newValue) !Array.isArray(newValue)
) { ) {
inputRef.current.value = newValue updateValue(newValue)
} }
}} }}
className="p-0 m-0 border-none hover:bg-primary/10 focus:bg-primary/10 dark:hover:bg-primary/20 dark:focus::bg-primary/20" className="p-0 m-0 border-none hover:bg-primary/10 focus:bg-primary/10 dark:hover:bg-primary/20 dark:focus::bg-primary/20"
data-testid="project-directory-button"
> >
<CustomIcon name="folder" className="w-5 h-5" /> <CustomIcon name="folder" className="w-5 h-5" />
<Tooltip position="inlineStart">Choose a folder</Tooltip> <Tooltip position="inlineStart">Choose a folder</Tooltip>
@ -264,13 +270,13 @@ export function createSettings() {
], ],
})), })),
}, },
Component: ({ value, onChange }) => ( Component: ({ value, updateValue }) => (
<> <>
<select <select
id="camera-controls" id="camera-controls"
className="block w-full px-3 py-1 bg-transparent border border-chalkboard-30" className="block w-full px-3 py-1 bg-transparent border border-chalkboard-30"
value={value} value={value}
onChange={onChange} onChange={(e) => updateValue(e.target.value as CameraSystem)}
> >
{cameraSystems.map((program) => ( {cameraSystems.map((program) => (
<option key={program} value={program}> <option key={program} value={program}>

View File

@ -96,7 +96,7 @@ export interface SettingProps<T = unknown> {
*/ */
Component?: React.ComponentType<{ Component?: React.ComponentType<{
value: T value: T
onChange: ChangeEventHandler updateValue: (newValue: T) => void
}> }>
} }

View File

@ -172,8 +172,8 @@ const Home = () => {
}, },
}) })
}, [ }, [
settings.app.projectDirectory, settings.app.projectDirectory.current,
settings.projects.defaultProjectName, settings.projects.defaultProjectName.current,
send, send,
]) ])

View File

@ -492,16 +492,14 @@ function GeneratedSetting({
setting.Component && ( setting.Component && (
<setting.Component <setting.Component
value={setting[settingsLevel] || setting.getFallback(settingsLevel)} value={setting[settingsLevel] || setting.getFallback(settingsLevel)}
onChange={(e) => { updateValue={(newValue) => {
if ('value' in e.target) { send({
send({ type: `set.${category}.${settingName}`,
type: `set.${category}.${settingName}`, data: {
data: { level: settingsLevel,
level: settingsLevel, value: newValue,
value: e.target.value, },
}, } as unknown as Event<WildcardSetEvent>)
} as unknown as Event<WildcardSetEvent>)
}
}} }}
/> />
) )