Skip to content

Commit d771168

Browse files
committed
add wait for timeout action to editor
1 parent a25131b commit d771168

7 files changed

Lines changed: 133 additions & 2 deletions

File tree

src/codegen/estree/expressions.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,16 @@ export function string(value: string): ts.StringLiteral {
3838

3939
export function literal({
4040
value,
41-
}: NodeOptions<ts.Literal, 'value'>): ts.Literal {
41+
}: NodeOptions<ts.Literal, 'value'>): ts.Expression {
4242
switch (typeof value) {
4343
case 'string':
4444
return string(value)
4545

4646
case 'number':
47+
if (isNaN(value)) {
48+
return identifier('NaN')
49+
}
50+
4751
return {
4852
...baseProps,
4953
type: NodeType.Literal,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Popover, TextField } from '@radix-ui/themes'
2+
import { useState } from 'react'
3+
4+
import { FieldGroup } from '@/components/Form'
5+
6+
import { ValuePopoverBadge } from '../components'
7+
8+
import { toFieldErrors } from './utils'
9+
10+
interface TimeoutForm {
11+
timeout: number
12+
onChange: (timeout: number) => void
13+
}
14+
15+
export function TimeoutForm({ timeout, onChange }: TimeoutForm) {
16+
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
17+
const [isTouched, setIsTouched] = useState(false)
18+
19+
const error = validateTimeout(timeout)
20+
21+
return (
22+
<Popover.Root
23+
open={isPopoverOpen}
24+
onOpenChange={(open) => {
25+
setIsPopoverOpen(open)
26+
if (!open) {
27+
setIsTouched(true)
28+
}
29+
}}
30+
>
31+
<Popover.Trigger>
32+
<ValuePopoverBadge
33+
displayValue={
34+
<>{isNaN(timeout) ? 'Enter a timeout' : `${timeout} ms`}</>
35+
}
36+
error={error}
37+
/>
38+
</Popover.Trigger>
39+
<Popover.Content align="start" size="1" width="300px">
40+
<FieldGroup
41+
name="timeout"
42+
label="Timeout (ms)"
43+
labelSize="1"
44+
mb="0"
45+
errors={toFieldErrors('timeout', isTouched ? error : undefined)}
46+
>
47+
<TextField.Root
48+
size="1"
49+
name="timeout"
50+
type="number"
51+
value={isNaN(timeout) ? '' : timeout}
52+
onChange={(e) => {
53+
const trimmed = e.target.value.trim()
54+
55+
if (!trimmed) {
56+
onChange(NaN)
57+
58+
return
59+
}
60+
61+
onChange(Number(trimmed))
62+
}}
63+
onBlur={() => setIsTouched(true)}
64+
/>
65+
</FieldGroup>
66+
</Popover.Content>
67+
</Popover.Root>
68+
)
69+
}
70+
71+
function validateTimeout(value: number) {
72+
if (isNaN(value)) {
73+
return 'Timeout must be a number'
74+
}
75+
76+
if (value < 0) {
77+
return 'Timeout must be >= 0'
78+
}
79+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Grid } from '@radix-ui/themes'
2+
3+
import { PageWaitForTimeoutAction } from '@/main/runner/schema'
4+
5+
import { TimeoutForm } from '../../ActionForms/forms/TimeoutForm'
6+
import { WithEditorMetadata } from '../../types'
7+
8+
interface WaitForTimeoutActionBodyProps {
9+
action: WithEditorMetadata<PageWaitForTimeoutAction>
10+
onChange: (action: WithEditorMetadata<PageWaitForTimeoutAction>) => void
11+
}
12+
13+
export function WaitForTimeoutActionBody({
14+
action,
15+
onChange,
16+
}: WaitForTimeoutActionBodyProps) {
17+
const handleTimeoutChange = (timeout: number) => {
18+
onChange({ ...action, timeout })
19+
}
20+
21+
return (
22+
<Grid columns="max-content auto max-content" gap="2" align="center">
23+
Wait for
24+
<TimeoutForm timeout={action.timeout} onChange={handleTimeoutChange} />
25+
</Grid>
26+
)
27+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './WaitForTimeoutActionBody'

src/views/BrowserTestEditor/Actions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export * from './FillAction'
44
export * from './GoToAction'
55
export * from './PageReloadAction'
66
export * from './UncheckAction'
7+
export * from './WaitForTimeoutAction'
78
export * from './WaitForAction'

src/views/BrowserTestEditor/EditableBrowserActionList.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,20 @@ function NewActionMenu({ onAddAction }: NewActionMenuProps) {
106106
Uncheck input
107107
</DropdownMenu.Item>
108108
<DropdownMenu.Separator />
109-
110109
<DropdownMenu.Item
111110
onClick={() => {
112111
onAddAction('locator.waitFor')
113112
}}
114113
>
115114
Wait for element
116115
</DropdownMenu.Item>
116+
<DropdownMenu.Item
117+
onClick={() => {
118+
onAddAction('page.waitForTimeout')
119+
}}
120+
>
121+
Wait for timeout
122+
</DropdownMenu.Item>
117123
<DropdownMenu.Separator />
118124
<DropdownMenu.Item
119125
onClick={() => {

src/views/BrowserTestEditor/actionEditorRegistry.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Code } from '@radix-ui/themes'
22
import {
33
CircleQuestionMarkIcon,
4+
ClockIcon,
45
GlobeIcon,
56
MousePointerClickIcon,
67
RefreshCwIcon,
@@ -19,6 +20,7 @@ import {
1920
PageReloadActionBody,
2021
UncheckActionBody,
2122
WaitForActionBody,
23+
WaitForTimeoutActionBody,
2224
} from './Actions'
2325
import { BrowserActionInstance } from './types'
2426
import { createDefaultLocatorOptions } from './utils'
@@ -150,6 +152,17 @@ const actionEditors: ActionEditorRegistry = {
150152
method: 'page.reload',
151153
}),
152154
},
155+
'page.waitForTimeout': {
156+
icon: <ClockIcon aria-hidden="true" />,
157+
render: ({ action, onChange }) => (
158+
<WaitForTimeoutActionBody action={action} onChange={onChange} />
159+
),
160+
create: () => ({
161+
id: crypto.randomUUID(),
162+
method: 'page.waitForTimeout',
163+
timeout: 1000,
164+
}),
165+
},
153166
'locator.waitFor': {
154167
icon: <TimerIcon aria-hidden="true" />,
155168
render: ({ action, onChange }) => (

0 commit comments

Comments
 (0)