Skip to content

Commit

Permalink
internal(Data files): Select data files in Test options (#441)
Browse files Browse the repository at this point in the history
  • Loading branch information
going-confetti authored Jan 30, 2025
1 parent 963dd6e commit c01750a
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/codegen/codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe('Code generation', () => {
},
testData: {
variables: [],
files: [],
},
rules: [],
allowlist: [],
Expand Down
2 changes: 1 addition & 1 deletion src/schemas/generator/v0/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function migrate(generator: GeneratorSchema): v1.GeneratorSchema {
version: '1.0',
recordingPath: generator.recordingPath,
options: generator.options,
testData: generator.testData,
testData: { ...generator.testData, files: [] },
rules: generator.rules,
allowlist: generator.allowlist,
includeStaticAssets: generator.includeStaticAssets,
Expand Down
9 changes: 9 additions & 0 deletions src/schemas/generator/v1/testData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ export const VariableSchema = z.object({
value: z.string(),
})

export const DataFileSchema = z.object({
name: z
.string()
.min(1, { message: 'Required' })
// Don't allow native object properties, like __proto__, valueOf, etc.
.refine((val) => !(val in {}), { message: 'Invalid name' }),
})

export const TestDataSchema = z.object({
variables: VariableSchema.array().superRefine((variables, ctx) => {
const names = variables.map((variable) => variable.name)
Expand All @@ -26,4 +34,5 @@ export const TestDataSchema = z.object({
})
}
}),
files: DataFileSchema.array().default([]),
})
3 changes: 2 additions & 1 deletion src/store/generator/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function selectGeneratorData(state: GeneratorStore): GeneratorFileData {
sleepType,
timing,
variables,
files,
recordingPath,
rules,
allowlist,
Expand All @@ -60,7 +61,7 @@ export function selectGeneratorData(state: GeneratorStore): GeneratorFileData {
timing,
},
},
testData: { variables },
testData: { variables, files },
rules,
allowlist,
includeStaticAssets,
Expand Down
8 changes: 7 additions & 1 deletion src/store/generator/slices/testData.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { ImmerStateCreator } from '@/utils/typescript'
import { Variable, TestData } from '@/types/testData'
import { Variable, TestData, DataFile } from '@/types/testData'

interface Actions {
setVariables: (variables: Variable[]) => void
setFiles: (files: DataFile[]) => void
}

export type TestDataStore = TestData & Actions

export const createTestDataSlice: ImmerStateCreator<TestDataStore> = (set) => ({
variables: [],
files: [],

setVariables: (variables: Variable[]) =>
set((state) => {
state.variables = variables
}),
setFiles: (files: DataFile[]) =>
set((state) => {
state.files = files
}),
})
3 changes: 2 additions & 1 deletion src/store/generator/useGeneratorStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const useGeneratorStore = create<GeneratorStore>()(
setGeneratorFile: (
{
options: { thinkTime, loadProfile },
testData: { variables },
testData: { variables, files },
recordingPath,
rules,
allowlist,
Expand Down Expand Up @@ -69,6 +69,7 @@ export const useGeneratorStore = create<GeneratorStore>()(
}
// data
state.variables = variables
state.files = files
// recording
state.requests = recording
state.recordingPath = recordingPath
Expand Down
3 changes: 3 additions & 0 deletions src/test/factories/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function createGeneratorData(
scriptName: 'script.js',
testData: {
variables: [],
files: [],
},
version: '1.0',
thresholds: [],
Expand Down Expand Up @@ -85,7 +86,9 @@ export function createGeneratorState(
setTiming: vi.fn(),

variables: [],
files: [],
setVariables: vi.fn(),
setFiles: vi.fn(),

vus: 1,
setVus: vi.fn(),
Expand Down
7 changes: 6 additions & 1 deletion src/types/testData.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { z } from 'zod'
import { VariableSchema, TestDataSchema } from '@/schemas/generator'
import {
VariableSchema,
TestDataSchema,
DataFileSchema,
} from '@/schemas/generator'

export type Variable = z.infer<typeof VariableSchema>
export type TestData = z.infer<typeof TestDataSchema>
export type DataFile = z.infer<typeof DataFileSchema>

export type DataRecord = Record<
string,
Expand Down
1 change: 1 addition & 0 deletions src/utils/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function createNewGeneratorFile(recordingPath = ''): GeneratorFileData {
},
testData: {
variables: [],
files: [],
},
rules: [createEmptyRule('verification')],
allowlist: [],
Expand Down
84 changes: 84 additions & 0 deletions src/views/Generator/TestOptions/DataFiles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { FieldGroup } from '@/components/Form'
import { TestDataSchema } from '@/schemas/generator'
import { useGeneratorStore } from '@/store/generator'
import { useStudioUIStore } from '@/store/ui'
import { TestData } from '@/types/testData'
import { zodResolver } from '@hookform/resolvers/zod'
import { CheckboxGroup, Text } from '@radix-ui/themes'
import { useCallback, useEffect } from 'react'
import { useForm } from 'react-hook-form'

export function DataFiles() {
const availableFiles = useStudioUIStore((store) => store.dataFiles)
const files = useGeneratorStore((store) => store.files)
const setFiles = useGeneratorStore((store) => store.setFiles)

const {
register,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<Pick<TestData, 'files'>>({
resolver: zodResolver(TestDataSchema.pick({ files: true })),
shouldFocusError: false,
defaultValues: {
files,
},
})

const options = [...availableFiles.values()].map((file) => ({
label: file.displayName,
value: file.displayName,
}))
const value = watch('files').map(({ name }) => name)

const onSubmit = useCallback(
(data: Pick<TestData, 'files'>) => {
setFiles(data.files)
},
[setFiles]
)

// Submit onChange
useEffect(() => {
const subscription = watch(() => handleSubmit(onSubmit)())
return () => subscription.unsubscribe()
}, [watch, handleSubmit, onSubmit])

const handleChange = (value: string[]) => {
setValue(
'files',
value.map((fileName) => ({
name: fileName,
}))
)
}

const filesField = register('files')

return (
<div>
<Text size="2" as="p" mb="2">
Make your test more realistic and prevent server-side caching from
affecting your results by using data files.
</Text>
<form onSubmit={handleSubmit(onSubmit)}>
<FieldGroup name="files" label="Files" errors={errors}>
<CheckboxGroup.Root
value={value}
onValueChange={handleChange}
onBlur={filesField.onBlur}
ref={filesField.ref}
>
{options.map((option) => (
<CheckboxGroup.Item key={option.value} value={option.value}>
{option.label}
</CheckboxGroup.Item>
))}
</CheckboxGroup.Root>
</FieldGroup>
</form>
</div>
)
}
17 changes: 13 additions & 4 deletions src/views/Generator/TestOptions/TestOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { LoadProfile } from './LoadProfile'
import { ThinkTime } from './ThinkTime'
import { VariablesEditor } from './VariablesEditor'
import { PopoverDialog } from '@/components/PopoverDialogs'
import { Feature } from '@/components/Feature'
import { Thresholds } from '../Thresholds'
import { Thresholds } from './Thresholds'
import { DataFiles } from './DataFiles'
import { useFeaturesStore } from '@/store/features'
import { Feature } from '@/components/Feature'

export function TestOptions() {
const { thresholds: isThresholdsEnabled } = useFeaturesStore(
Expand Down Expand Up @@ -38,7 +39,10 @@ export function TestOptions() {
<Tabs.Trigger value="thresholds">Thresholds</Tabs.Trigger>
</Feature>
<Tabs.Trigger value="thinkTime">Think time</Tabs.Trigger>
<Tabs.Trigger value="testData">Test data</Tabs.Trigger>
<Tabs.Trigger value="variables">Variables</Tabs.Trigger>
<Feature feature="data-files">
<Tabs.Trigger value="dataFiles">Data files</Tabs.Trigger>
</Feature>
</Tabs.List>
<ScrollArea
scrollbars="vertical"
Expand All @@ -53,9 +57,14 @@ export function TestOptions() {
<Tabs.Content value="thinkTime">
<ThinkTime />
</Tabs.Content>
<Tabs.Content value="testData">
<Tabs.Content value="variables">
<VariablesEditor />
</Tabs.Content>
<Feature feature="data-files">
<Tabs.Content value="dataFiles">
<DataFiles />
</Tabs.Content>
</Feature>
<Tabs.Content value="thresholds">
<Thresholds />
</Tabs.Content>
Expand Down
File renamed without changes.
16 changes: 7 additions & 9 deletions src/views/Generator/TestOptions/VariablesEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ export function VariablesEditor() {
control,
watch,
formState: { errors },
} = useForm<TestData>({
resolver: zodResolver(TestDataSchema),
} = useForm<Pick<TestData, 'variables'>>({
resolver: zodResolver(TestDataSchema.pick({ variables: true })),
shouldFocusError: false,
defaultValues: {
variables,
},
defaultValues: { variables },
})

const { fields, append, remove } = useFieldArray({
Expand All @@ -49,7 +47,7 @@ export function VariablesEditor() {
const watchVariables = watch('variables')

const onSubmit = useCallback(
(data: TestData) => {
(data: Pick<TestData, 'variables'>) => {
setVariables(data.variables)
},
[setVariables]
Expand Down Expand Up @@ -112,10 +110,10 @@ function VariableRow({
register,
remove,
}: {
field: FieldArrayWithId<TestData, 'variables', 'id'>
field: FieldArrayWithId<Pick<TestData, 'variables'>, 'variables', 'id'>
index: number
register: UseFormRegister<TestData>
errors: FieldErrors<TestData>
register: UseFormRegister<Pick<TestData, 'variables'>>
errors: FieldErrors<Pick<TestData, 'variables'>>
remove: UseFieldArrayRemove
}) {
const isVariableInUse = useGeneratorStore((state) =>
Expand Down

0 comments on commit c01750a

Please sign in to comment.