Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
a3225ce
merge all save handlers into one
allansson Mar 4, 2026
458fdd8
handle notifications in renderer
allansson Mar 4, 2026
8e19279
use single open handler
allansson Mar 4, 2026
b87fe29
analyze script in separate step
allansson Mar 4, 2026
c2294d9
use shared hook to get filename
allansson Mar 4, 2026
0f0a6ae
use studio file throughout
allansson Mar 4, 2026
af2ceea
use absolute path in router
allansson Mar 4, 2026
e8dcb51
make recording path relative to generator
allansson Mar 4, 2026
e75d396
store data files as relative paths
allansson Mar 4, 2026
36d4eb6
split proxydata and jsonpaths dependency
allansson Mar 4, 2026
2e42291
parse and serialize har in open/save handlers
allansson Mar 4, 2026
81c3647
use single handler for running scripts
allansson Mar 5, 2026
ada4d70
make script execution (mostly) independent of directory
allansson Mar 5, 2026
738b024
add handler to get temp path
allansson Mar 5, 2026
7950211
remove all usages of useScriptPreview but one
allansson Mar 5, 2026
0e5a9af
use path when exporting script
allansson Mar 5, 2026
dee70cc
generate scripts with temp paths when validating
allansson Mar 5, 2026
de9eb93
make codegen position independent
allansson Mar 5, 2026
ff80138
rewrite how recordings/generators/etc are fetched from dirs
allansson Mar 5, 2026
e853553
create workspace class
allansson Mar 5, 2026
af847bd
store current workspace on window
allansson Mar 5, 2026
4893c38
allow user to switch workspace
allansson Mar 6, 2026
de9fc2b
remove legacy filelocation
allansson Mar 6, 2026
6b8d4c8
add workspace icon to activity bar
allansson Mar 6, 2026
4e8778b
add file tree for workspace
allansson Mar 6, 2026
ed90b42
open data files using file.open handler
allansson Mar 6, 2026
828d88b
always infer file type from extension
allansson Mar 6, 2026
e8678e5
remove all special-case open preloads
allansson Mar 6, 2026
d31fdc2
use native file export when exporting scripts
allansson Mar 6, 2026
1fa1626
add one route to rule them all
allansson Mar 6, 2026
6c6866e
use shared route for debugger
allansson Mar 6, 2026
f368ac4
use shared route for data files
allansson Mar 6, 2026
d1faaa5
use shared layout for browser tests
allansson Mar 6, 2026
4609fa4
use shared layout for generator view
allansson Mar 6, 2026
2d5a5d6
re-arrange sidepanel
allansson Mar 9, 2026
0ea98d3
use resizable panels
allansson Mar 9, 2026
a1ed58f
add recent urls to record view
allansson Mar 9, 2026
e0d210c
file specific icons
allansson Mar 9, 2026
e264adc
move save logic to editorview
allansson Mar 9, 2026
f64991e
new file
allansson Mar 9, 2026
5cc19ca
defer browser test file creation
allansson Mar 9, 2026
cc4d310
add open file command
allansson Mar 9, 2026
f76424e
add save command
allansson Mar 9, 2026
ae5713e
add save as command
allansson Mar 10, 2026
c2f41d3
center note supported message
allansson Mar 10, 2026
fa2bfb4
create files from file tree
allansson Mar 11, 2026
4a85704
keep state between navigations in activity bar
allansson Mar 12, 2026
c62d608
add new file menu
allansson Mar 12, 2026
3a974d0
Merge branch 'main' into hackathon/workspace
allansson Mar 12, 2026
e4e54a1
use micromatch to filter files
allansson Mar 12, 2026
636bf5e
implement rename file
allansson Mar 12, 2026
8a3305b
add to recent documents
allansson Mar 13, 2026
359dd2f
add workspace configuration handling
allansson Mar 13, 2026
7e90061
pull in some of nat's sidebar styling
allansson Mar 30, 2026
83860a9
spacing in recent urls
allansson Mar 30, 2026
a622494
Merge branch 'main' into hackathon/workspace
allansson Mar 30, 2026
7abf95c
create new folder from open workspace dialog
allansson Mar 30, 2026
48cbf48
new sidebar layout
allansson Mar 30, 2026
81d5bdf
handle fs changes
allansson Mar 30, 2026
121cf5d
create folders
allansson Mar 30, 2026
98bb53c
remove har import in favor of opening file
allansson Apr 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { ForgeConfig, ForgeMakeResult } from '@electron-forge/shared-types'
import path from 'path'

import { CUSTOM_APP_PROTOCOL } from './src/main/deepLinks.constants'
import { getPlatform, getArch } from './src/utils/electron'
import { getPlatform, getArch } from './src/utils/platform'
import { windowsSign } from './windowsSign'
import { spawnSignFile } from './windowsSignHook'

Expand Down
119 changes: 85 additions & 34 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
"@types/diff": "^7.0.2",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/express": "^5.0.3",
"@types/graceful-fs": "^4.1.9",
"@types/har-format": "^1.2.16",
"@types/k6": "^1.3.1",
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.10",
"@types/node-forge": "^1.3.11",
"@types/papaparse": "^5.3.15",
"@types/react": "^19.2.10",
Expand Down Expand Up @@ -92,6 +94,7 @@
"@emotion/styled": "^11.13.0",
"@hookform/error-message": "^2.0.1",
"@hookform/resolvers": "^3.9.0",
"@iarna/toml": "^2.2.5",
"@medv/finder": "^4.0.2",
"@microlink/react-json-view": "^1.27.0",
"@monaco-editor/react": "^4.7.0",
Expand Down Expand Up @@ -133,17 +136,20 @@
"express": "^5.2.1",
"find-process": "^1.4.7",
"fuse.js": "^7.0.0",
"graceful-fs": "^4.2.11",
"https-proxy-agent": "^7.0.6",
"immer": "^10.1.1",
"jsonrepair": "^3.8.0",
"keyboardjs": "^2.7.0",
"lodash-es": "^4.17.21",
"lucide-react": "^0.562.0",
"micromatch": "^4.0.8",
"monaco-editor": "^0.55.1",
"nanoid": "^5.1.6",
"node-forge": "^1.3.2",
"openid-client": "^6.1.7",
"papaparse": "^5.5.1",
"pathe": "^2.0.3",
"plist": "^3.1.0",
"prettier": "^3.3.2",
"react": "^19.2.4",
Expand All @@ -153,6 +159,7 @@
"react-router-dom": "^6.30.3",
"react-select": "^5.10.2",
"react-use": "^17.5.1",
"readdirp": "^4.0.1",
"rrweb": "^2.0.0-alpha.18",
"tiny-invariant": "^1.3.3",
"tree-kill": "^1.2.2",
Expand Down
16 changes: 12 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { DevToolsDialog } from '@/components/DevToolsDialog'
import { SettingsDialog } from '@/components/Settings/SettingsDialog'
import { Toasts } from '@/components/Toast/Toasts'
import { Theme as StudioTheme } from '@/components/primitives/Theme'
import { useCloseSplashScreen } from '@/hooks/useCloseSplashScreen'
import { WorkspaceProvider, useWorkspace } from '@/contexts/WorkspaceContext'
import { useTheme } from '@/hooks/useTheme'
import { queryClient } from '@/utils/query'

Expand All @@ -16,20 +16,28 @@ import { globalStyles } from './globalStyles'

enableMapSet()

export function App() {
function AppContent() {
const theme = useTheme()
useCloseSplashScreen()
const workspace = useWorkspace()

return (
<QueryClientProvider client={queryClient}>
<StudioTheme />
<RadixTheme accentColor="orange" appearance={theme}>
<Global styles={globalStyles} />
<Toasts />
<AppRoutes />
<AppRoutes key={workspace?.path} />
<DevToolsDialog />
<SettingsDialog />
</RadixTheme>
</QueryClientProvider>
)
}

export function App() {
return (
<WorkspaceProvider>
<AppContent />
</WorkspaceProvider>
)
}
22 changes: 6 additions & 16 deletions src/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@ import {
} from 'react-router-dom'

import { Layout } from '@/components/Layout/Layout'
import { BrowserTestEditor } from '@/views/BrowserTestEditor'
import { EditorView } from '@/views/EditorView'
import { Home } from '@/views/Home'
import { Recorder } from '@/views/Recorder'
import { RecordingPreviewer } from '@/views/RecordingPreviewer'
import { Validator } from '@/views/Validator'

import { ErrorElement } from './ErrorElement'
import { routeMap } from './routeMap'
import { DataFile } from './views/DataFile'
import { Generator } from './views/Generator'
import { NewBrowserTestView } from './views/BrowserTestEditor/NewBrowserTestView'
import { NewGeneratorView } from './views/Generator/NewGeneratorView'

const router = createHashRouter(
createRoutesFromChildren(
Expand All @@ -29,17 +27,9 @@ const router = createHashRouter(
>
<Route index element={<Home />} />
<Route path={routeMap.recorder} element={<Recorder />} />
<Route
path={routeMap.recordingPreviewer}
element={<RecordingPreviewer />}
/>
<Route path={routeMap.generator} element={<Generator />} />
<Route
path={routeMap.browserTestEditor}
element={<BrowserTestEditor />}
/>
<Route path={routeMap.validator} element={<Validator />} />
<Route path={routeMap.dataFilePreviewer} element={<DataFile />} />
<Route path={routeMap.editorView} element={<EditorView />} />
<Route path={routeMap.newGenerator} element={<NewGeneratorView />} />
<Route path={routeMap.newBrowserTest} element={<NewBrowserTestView />} />
<Route path="*" element={<NoRouteFound />} />
</Route>
)
Expand Down
20 changes: 13 additions & 7 deletions src/codegen/codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ describe('Code generation', () => {

expect(
generateScript({
scriptPath: '/root/script.js',
recording: [],
generator: {
version: '2.0',
version: '3.0',
recordingPath: 'test',
options: {
loadProfile: {
Expand Down Expand Up @@ -118,7 +119,7 @@ describe('Code generation', () => {

describe('generateImports', () => {
const generator: GeneratorFileData = {
version: '2.0',
version: '3.0',
recordingPath: 'test',
options: {
loadProfile: {
Expand Down Expand Up @@ -162,7 +163,7 @@ describe('Code generation', () => {
})

it('should generate imports with data files', async () => {
const files = [{ name: 'users.csv' }, { name: 'products.json' }]
const files = [{ path: 'users.csv' }, { path: 'products.json' }]
const expectedResult = await prettify(`
import { group, sleep, check } from 'k6'
import http from 'k6/http'
Expand Down Expand Up @@ -221,7 +222,10 @@ describe('Code generation', () => {

describe('generateDataFileDeclarations', () => {
it('should generate file declarations', async () => {
const files = [{ name: 'users.csv' }, { name: 'products.json' }]
const files = [
{ path: '/Root/Data/users.csv' },
{ path: '/Root/Data/products.json' },
]

const expectedResult = await prettify(`
const FILES = {
Expand All @@ -233,9 +237,11 @@ describe('Code generation', () => {
}),
};`)

expect(await prettify(generateDataFileDeclarations(files))).toBe(
expectedResult
)
expect(
await prettify(
generateDataFileDeclarations(files, '/Root/Scripts/script.js')
)
).toBe(expectedResult)
})
})

Expand Down
32 changes: 23 additions & 9 deletions src/codegen/codegen.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as pathe from 'pathe'

import { K6_EXPORTS, REQUIRED_IMPORTS } from '@/constants/imports'
import { getCustomCodeSnippet } from '@/rules/parameterization'
import { applyRules } from '@/rules/rules'
Expand All @@ -8,6 +10,7 @@ import { DataFile, Variable } from '@/types/testData'
import { ThinkTime } from '@/types/testOptions'
import { getFileNameWithoutExtension } from '@/utils/file'
import { safeBtoa } from '@/utils/format'
import { makeRelativePath } from '@/utils/fs/path'
import { groupProxyData } from '@/utils/groups'
import { getContentTypeWithCharsetHeader } from '@/utils/headers'
import { exhaustive } from '@/utils/typescript'
Expand All @@ -22,11 +25,13 @@ import { generateImportStatement } from './imports'
import { generateOptions } from './options'

interface GenerateScriptParams {
scriptPath: string
recording: ProxyData[]
generator: GeneratorFileData
}

export function generateScript({
scriptPath,
recording,
generator,
}: GenerateScriptParams): string {
Expand All @@ -42,7 +47,7 @@ export function generateScript({
export const options = ${generateOptions(generator.options)}

${generateVariableDeclarations(generator.testData.variables)}
${generateDataFileDeclarations(generator.testData.files)}
${generateDataFileDeclarations(generator.testData.files, scriptPath)}
${generateGetUniqueItemFunction(generator.testData.files)}

export default function() {
Expand All @@ -59,11 +64,11 @@ export function generateImports(
generator: GeneratorFileData,
options: GenerateImportsOptions = {}
): string {
const hasCSVDataFiles = generator.testData.files.some(({ name }) =>
name.toLowerCase().endsWith('csv')
const hasCSVDataFiles = generator.testData.files.some((file: DataFile) =>
pathe.basename(file.path).toLowerCase().endsWith('csv')
)
const hasJSONDataFiles = generator.testData.files.some(({ name }) =>
name.toLowerCase().endsWith('json')
const hasJSONDataFiles = generator.testData.files.some((file: DataFile) =>
pathe.basename(file.path).toLowerCase().endsWith('json')
)
const imports = [
...REQUIRED_IMPORTS,
Expand Down Expand Up @@ -93,24 +98,33 @@ export function generateVariableDeclarations(variables: Variable[]): string {
return `const VARS = {\n${variableKeyValuePairs}\n};`
}

export function generateDataFileDeclarations(files: DataFile[]): string {
export function generateDataFileDeclarations(
files: DataFile[],
scriptPath: string | undefined
): string {
if (files.length === 0) {
return ''
}

const fileKeyValuePairs = files
.map(({ name }) => {
.map((file: DataFile) => {
const name = pathe.basename(file.path)
const displayName = getFileNameWithoutExtension(name)
const isCSV = name.toLowerCase().endsWith('csv')

// If the script path is not provided, just assume that the script is in the same
// directory as the data file.
const scriptDir = pathe.dirname(scriptPath ?? file.path)
const relativePath = makeRelativePath(scriptDir, file.path)

if (isCSV) {
return `
"${displayName}": await csv.parse(await fs.open('../Data/${name}'), { asObjects: true })`
"${displayName}": await csv.parse(await fs.open('${relativePath}'), { asObjects: true })`
}

return `
"${displayName}": new SharedArray("${displayName}", () => {
const data = JSON.parse(open('../Data/${name}'));
const data = JSON.parse(open('${relativePath}'));
return Array.isArray(data) ? data : [data];
})`
})
Expand Down
Loading