Skip to content

Commit 0e2756a

Browse files
Keep only the proven example-selection race; log Turnstile on CI
Revert SKIP_TURNSTILE/Caddy (it broke /service/control/health and was not proven as the 120s hang). Keep getCode() plus waiting for .rs-panel-in, with a local same-click repro. Add console markers around Turnstile so the next hung /run on CI shows whether execute() returned. Co-authored-by: Max Schmitt <max@schmitt.mx>
1 parent 545fda2 commit 0e2756a

12 files changed

Lines changed: 109 additions & 189 deletions

File tree

‎e2e/tests/visual.spec.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ test.describe("should handle platform core related features", () => {
167167

168168
await page.getByRole('button', { name: 'Run'}).click();
169169
await expect(page.getByText("Execution timeout!")).toBeVisible({
170-
timeout: 90 * 1000,
170+
timeout: 70 * 1000,
171171
});
172172
})
173173
test("should handle uncaughtException correctly", async ({ page }) => {

‎frontend/Caddyfile‎

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,16 @@
22

33
root * /frontend
44

5-
# Path-specific handles are mutually exclusive. A catch-all file_server
6-
# handle would 404 /service/control/* before reverse_proxy could run.
7-
handle /runtime-config.js {
8-
header Content-Type text/javascript
9-
header Cache-Control no-store
10-
respond "window.__TRY_PLAYWRIGHT__={skipTurnstile:{$SKIP_TURNSTILE:false}};"
11-
}
12-
13-
handle /service/control/* {
14-
reverse_proxy control:8080
15-
}
16-
17-
handle /file-uploads/* {
18-
reverse_proxy rustfs:9000 {
19-
header_up Host rustfs:9000
20-
method GET
21-
}
22-
}
23-
24-
handle {
25-
file_server
26-
}
5+
file_server
276

287
header *.js Cache-Control max-age=86400
298
header *.css Cache-Control max-age=86400
309
header *.tff Cache-Control max-age=86400
3110

3211
log
12+
13+
reverse_proxy /service/control/* control:8080
14+
reverse_proxy /file-uploads/* rustfs:9000 {
15+
header_up Host rustfs:9000
16+
method GET
17+
}

‎frontend/index.html‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,7 @@
77
<meta name="theme-color" content="#000000" />
88
<meta name="description" content="An interactive playground for Playwright with various examples available."/>
99
<title>Try Playwright</title>
10-
<script src="/runtime-config.js"></script>
11-
<script>
12-
if (!window.__TRY_PLAYWRIGHT__?.skipTurnstile) {
13-
const script = document.createElement('script');
14-
script.defer = true;
15-
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
16-
document.head.appendChild(script);
17-
}
18-
</script>
10+
<script defer src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
1911
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-34156117-11"></script>
2012
<script>
2113
if (!window.location.search.includes("no-analytics")) {

‎frontend/public/runtime-config.js‎

Lines changed: 0 additions & 1 deletion
This file was deleted.

‎frontend/src/components/App/index.tsx‎

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Col, Grid, IconButton, Loader, Panel, CustomProvider } from 'rsuite'
33
import PlayIcon from '@rsuite/icons/PlayOutline';
44

55
import { ExecutionResponse, runCode, trackEvent } from '../../utils'
6-
import { waitForTurnstileToken } from '../../turnstile'
76
import RightPanel from '../RightPanel'
87
import Header from '../Header'
98
import Editor from '../Editor'
@@ -22,39 +21,52 @@ const App: React.FunctionComponent = () => {
2221
const handleExecutionRef = useRef<() => Promise<void>>(undefined)
2322
const [darkMode] = useDarkMode()
2423
const turnstileRef = useRef<HTMLDivElement>(null)
25-
const skipTurnstile = Boolean(window.__TRY_PLAYWRIGHT__?.skipTurnstile)
2624

2725
const handleExecution = async (): Promise<void> => {
2826
setLoading(true)
2927
setResponse(null)
3028

3129
trackEvent()
32-
const turnstileToken = skipTurnstile
33-
? ''
34-
: await waitForTurnstileToken({
35-
turnstile: window.turnstile,
36-
container: turnstileRef.current,
37-
sitekey: VITE_TURNSTILE_SITEKEY,
38-
})
30+
const started = Date.now()
31+
console.info('[try-playwright] run: before-turnstile', {
32+
hasExecute: typeof (window as any).turnstile?.execute,
33+
elapsedMs: 0,
34+
})
35+
const turnstileToken = await new Promise<string>((resolve) => {
36+
try {
37+
(window as any).turnstile.reset();
38+
} catch (error) {}
39+
(window as any).turnstile.execute(turnstileRef.current, {
40+
sitekey: VITE_TURNSTILE_SITEKEY,
41+
callback: (token: string) => {
42+
console.info('[try-playwright] run: turnstile-callback', {
43+
elapsedMs: Date.now() - started,
44+
tokenLength: token ? token.length : 0,
45+
})
46+
resolve(token)
47+
},
48+
'error-callback': () => {
49+
console.info('[try-playwright] run: turnstile-error-callback', {
50+
elapsedMs: Date.now() - started,
51+
})
52+
resolve('')
53+
},
54+
});
55+
});
3956
const codeToRun = getCode()
40-
try {
41-
setResponse(await runCode(codeToRun, codeLanguage, turnstileToken))
42-
} finally {
43-
setLoading(false)
44-
onChangeRightPanelMode(false)
45-
}
57+
console.info('[try-playwright] run: posting', {
58+
elapsedMs: Date.now() - started,
59+
codeLength: codeToRun.length,
60+
})
61+
setResponse(await runCode(codeToRun, codeLanguage, turnstileToken))
62+
setLoading(false)
63+
onChangeRightPanelMode(false)
4664
}
4765
handleExecutionRef.current = handleExecution
4866

4967
return (
5068
<CustomProvider theme={darkMode ? 'dark' : 'light'}>
51-
{!skipTurnstile && (
52-
<div
53-
ref={turnstileRef}
54-
aria-hidden="true"
55-
style={{ position: 'fixed', left: 0, bottom: 0, width: 300, height: 65, opacity: 0.01, pointerEvents: 'none', overflow: 'hidden' }}
56-
/>
57-
)}
69+
<div ref={turnstileRef} style={{ display: 'none' }} />
5870
<Header />
5971
<Grid fluid className={styles.grid}>
6072
<Col span={{ xs: 24, md: 12 }}>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { test, expect } from '@playwright/experimental-ct-react'
2+
import { StaleCodeRefRepro } from './staleCodeRef'
3+
4+
test('render state is stale in the same click as onChange; getCode is not', async ({ mount }) => {
5+
const component = await mount(<StaleCodeRefRepro />)
6+
await component.getByRole('button', { name: 'select-and-read' }).click()
7+
await expect(component.getByTestId('from-render')).toHaveText('')
8+
await expect(component.getByTestId('from-ref')).toHaveText('example-8-code')
9+
})
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { createContext, useContext, useRef, useState, type ReactNode } from 'react'
2+
3+
type LatestCode = {
4+
code: string
5+
getCode: () => string
6+
onChange: (value: string) => void
7+
}
8+
9+
const Ctx = createContext<LatestCode>({
10+
code: '',
11+
getCode: () => '',
12+
onChange: (_value: string) => {},
13+
})
14+
15+
const Provider = ({ children }: { children: ReactNode }) => {
16+
const [code, setCode] = useState('')
17+
const latestCode = useRef(code)
18+
const onChange = (next: string) => {
19+
latestCode.current = next
20+
setCode(next)
21+
}
22+
return (
23+
<Ctx.Provider value={{ code, getCode: () => latestCode.current, onChange }}>
24+
{children}
25+
</Ctx.Provider>
26+
)
27+
}
28+
29+
const Probe = () => {
30+
const { code, getCode, onChange } = useContext(Ctx)
31+
const [fromRender, setFromRender] = useState('unset')
32+
const [fromRef, setFromRef] = useState('unset')
33+
return (
34+
<div>
35+
<button
36+
onClick={() => {
37+
onChange('example-8-code')
38+
setFromRender(code)
39+
setFromRef(getCode())
40+
}}
41+
>
42+
select-and-read
43+
</button>
44+
<div data-testid="from-render">{fromRender}</div>
45+
<div data-testid="from-ref">{fromRef}</div>
46+
</div>
47+
)
48+
}
49+
50+
/** Story used by staleCodeRef.spec.tsx — not production UI. */
51+
export const StaleCodeRefRepro = () => (
52+
<Provider>
53+
<Probe />
54+
</Provider>
55+
)

‎frontend/src/turnstile.spec.ts‎

Lines changed: 0 additions & 46 deletions
This file was deleted.

‎frontend/src/turnstile.ts‎

Lines changed: 0 additions & 62 deletions
This file was deleted.

‎frontend/src/vite-env.d.ts‎

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1 @@
11
/// <reference types="vite/client" />
2-
3-
interface TryPlaywrightRuntimeConfig {
4-
skipTurnstile?: boolean
5-
}
6-
7-
interface Window {
8-
__TRY_PLAYWRIGHT__?: TryPlaywrightRuntimeConfig
9-
monacoEditorModel?: {
10-
getValue?: () => string
11-
setValue?: (value: string) => void
12-
}
13-
turnstile?: {
14-
reset: (container?: HTMLElement | null) => void
15-
execute: (container: HTMLElement | null, options?: Record<string, unknown>) => void
16-
}
17-
}
18-

0 commit comments

Comments
 (0)