Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions public/robots.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
User-agent: *
Disallow: /playground/
Disallow: /sitemap/
3 changes: 2 additions & 1 deletion src/playground/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function viewLabel(view: PlaygroundViewType): string {
export default function Playground({
setPlaygroundState,
playgroundState,
onEditorFocus,
}: PlaygroundProps) {
const [leftDrawerOpen, setLeftDrawerOpen] = useState(false);
const [rightDrawerOpen, setRightDrawerOpen] = useState(false);
Expand Down Expand Up @@ -253,7 +254,7 @@ export default function Playground({
placeholder="Enter your code here"
onUpdate={onUpdate}
onChange={onChange}
autoFocus={true}
onFocusCapture={onEditorFocus}
data-testid="editor"
gritQueryMatches={gritQueryResults.matches}
/>
Expand Down
49 changes: 31 additions & 18 deletions src/playground/PlaygroundLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { RuleDomainValue } from "@biomejs/wasm-web";
import {
type Dispatch,
type SetStateAction,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import LoadingScreen from "@/playground/components/LoadingScreen";
import Playground from "@/playground/Playground";
import {
type ArrowParentheses,
Expand Down Expand Up @@ -58,13 +58,18 @@ function PlaygroundLoader() {
const [loadingState, setLoadingState] = useState<LoadingState>(
LoadingState.Loading,
);
const [loadRequested, setLoadRequested] = useState(false);
const [state, setPlaygroundState, resetPlaygroundState] =
usePlaygroundState();
const workerRef = useRef<Worker | null>(null);
const prettierWorkerRef = useRef<Worker | null>(null);

// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies mismatch
useEffect(() => {
if (!loadRequested) {
return;
}

workerRef.current = new Worker(
new URL("./workers/biomeWorker", import.meta.url),
{ type: "module" },
Expand Down Expand Up @@ -188,7 +193,7 @@ function PlaygroundLoader() {
workerRef.current?.terminate();
prettierWorkerRef.current?.terminate();
};
}, []);
}, [loadRequested]);

// Dispatch updated settings
// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies mismatch
Expand Down Expand Up @@ -277,22 +282,30 @@ function PlaygroundLoader() {
getFileState(state, state.currentFile)?.gritQuery,
]);

switch (loadingState) {
case LoadingState.Error:
return <div>Error loading. Please refresh</div>;

case LoadingState.Loading:
return <LoadingScreen />;

default:
return (
<Playground
resetPlaygroundState={resetPlaygroundState}
setPlaygroundState={setPlaygroundState}
playgroundState={state}
/>
);
}
const loadPlayground = useCallback(() => {
setLoadRequested(true);
}, []);

return (
<>
<Playground
resetPlaygroundState={resetPlaygroundState}
setPlaygroundState={setPlaygroundState}
playgroundState={state}
onEditorFocus={loadPlayground}
/>
{loadRequested && loadingState === LoadingState.Loading && (
<div className="playground-load-status" role="status">
Loading Biome…
</div>
)}
{loadingState === LoadingState.Error && (
<div className="playground-load-status error" role="alert">
Error loading Biome. Please refresh.
</div>
)}
</>
);
}

function buildLocation(state: PlaygroundState): string {
Expand Down
3 changes: 0 additions & 3 deletions src/playground/components/LoadingScreen.tsx

This file was deleted.

1 change: 1 addition & 0 deletions src/playground/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ export interface PlaygroundProps {
setPlaygroundState: Dispatch<SetStateAction<PlaygroundState>>;
resetPlaygroundState: () => void;
playgroundState: PlaygroundState;
onEditorFocus: () => void;
}

export type Tree = ReturnType<typeof parser.parse>;
Expand Down
36 changes: 21 additions & 15 deletions src/styles/playground/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,41 @@ body {
width: 100%;
height: 100%;
}
.loading {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
}

#root {
position: relative;
display: flex;
width: 100%;
height: calc(100vh - 65px); /* header size + border */
}

.playground-load-status {
position: absolute;
z-index: 10;
top: 0.75rem;
left: 50%;
transform: translateX(-50%);
border: 1px solid var(--sl-color-gray-5);
border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
background: var(--sl-color-bg);
box-shadow: var(--sl-shadow-md);
font-size: 0.875rem;
pointer-events: none;
}

.playground-load-status.error {
border-color: var(--sl-color-red-high);
color: var(--sl-color-red-high);
}

body {
display: flex;
flex-direction: column;
overscroll-behavior-y: none;
height: 100vh;
width: 100%;

.loading {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
}

#root {
display: flex;
}
Expand Down
48 changes: 48 additions & 0 deletions tests/playground/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ function encodeCode(code: string): string {
return Buffer.from(code, "utf16le").toString("base64");
}

async function activatePlayground(page: Page) {
await page.getByTestId("editor").getByRole("textbox").focus();
const loadStatus = page.locator(".playground-load-status");
await expect(loadStatus).toHaveText("Loading Biome…");
await expect(loadStatus).toHaveCount(0, { timeout: 30_000 });
}

/** Toggles a view from the toolbar, expanding the "Internals" group first if needed. */
async function toggleView(
page: Page,
Expand All @@ -19,12 +26,35 @@ async function toggleView(
await button.click(options.additive ? { modifiers: ["Shift"] } : {});
}

test("loads the Biome WASM after editor focus", async ({ page }) => {
const wasmRequests: string[] = [];
page.on("request", (request) => {
const url = request.url();
if (url.includes("biome_wasm_bg") && url.includes(".wasm")) {
wasmRequests.push(url);
}
});

await page.goto("/playground");
await page.waitForLoadState("networkidle");
await expect(
page.getByTestId("editor").getByRole("textbox"),
).not.toBeFocused();
expect(wasmRequests).toEqual([]);

await activatePlayground(page);
await expect
.poll(() => wasmRequests.length, { timeout: 30_000 })
.toBeGreaterThan(0);
});

test.describe("playground should format code", () => {
test.describe("on navigation", () => {
test("javascript", async ({ page }) => {
await page.goto(
"/playground?code=bABlAHQAIABhACAAPQAgADUAOwA%3D&prettier=true",
);
await activatePlayground(page);
await expect(
page.getByTestId("biome-output").getByRole("textbox"),
).toContainText("let a = 5;");
Expand All @@ -37,6 +67,7 @@ test.describe("playground should format code", () => {
await page.goto(
"/playground?files.main.css=ZABpAHYAIAB7AGMAbwBsAG8AcgA6ACAAYgBsAHUAZQA7AH0A&prettier=true",
);
await activatePlayground(page);
await expect(
page.getByTestId("biome-output").getByRole("textbox"),
).toContainText("div { color: blue;}");
Expand All @@ -49,6 +80,7 @@ test.describe("playground should format code", () => {
await page.goto(
"/playground?files.main.html=PABkAGkAdgA%2BADwALwBkAGkAdgA%2BAA%3D%3D&prettier=true",
);
await activatePlayground(page);
await expect(
page.getByTestId("biome-output").getByRole("textbox"),
).toContainText("<div></div>");
Expand All @@ -63,6 +95,7 @@ test.describe("playground should format code", () => {
"files.main.md": encodeCode("one\ntwo\n"),
});
await page.goto(`/playground?prettier=true#${hash}`);
await activatePlayground(page);
const biomeOutput = page.getByTestId("biome-output").getByRole("textbox");
const prettierOutput = page
.getByTestId("prettier-output")
Expand Down Expand Up @@ -93,6 +126,7 @@ test.describe("playground should format code", () => {
await page.goto(
`/playground?prettier=true#code=${encodeURIComponent(encodeCode("let a=5"))}`,
);
await activatePlayground(page);
await expect(page.getByTestId("prettier-diff-hint")).toHaveText(
"Exact match",
);
Expand All @@ -101,6 +135,7 @@ test.describe("playground should format code", () => {
test.describe("on typing", () => {
test("javascript", async ({ page }) => {
await page.goto("/playground");
await activatePlayground(page);
await page.getByLabel("Compare Prettier").check();
await page.getByTestId("editor").getByRole("textbox").fill("let a = 5;");
await expect(
Expand All @@ -118,6 +153,7 @@ test.describe("playground should show formatter IR", () => {

test("javascript", async ({ page }) => {
await page.goto("/playground?code=bABlAHQAIABhACAAPQAgADUAOwA%3D");
await activatePlayground(page);
await toggleView(page, "Formatter IR");
await page
.locator(".playground-view-pane")
Expand All @@ -135,6 +171,7 @@ test.describe("playground should show formatter IR", () => {
await page.goto(
"/playground?files.main.css=ZABpAHYAIAB7AGMAbwBsAG8AcgA6ACAAYgBsAHUAZQA7AH0A",
);
await activatePlayground(page);
await toggleView(page, "Formatter IR");
await page
.locator(".playground-view-pane")
Expand All @@ -152,6 +189,7 @@ test.describe("playground should show formatter IR", () => {
await page.goto(
"/playground?files.main.html=PABkAGkAdgA%2BADwALwBkAGkAdgA%2BAA%3D%3D",
);
await activatePlayground(page);
await toggleView(page, "Formatter IR");
await page
.locator(".playground-view-pane")
Expand Down Expand Up @@ -234,6 +272,7 @@ test.describe("playground links", () => {
const popup = page.waitForEvent("popup");
await link.click();
const playground = await popup;
await activatePlayground(playground);
await expect(
playground.getByTestId("editor").locator(".cm-line"),
).toHaveText(code.split("\n"));
Expand Down Expand Up @@ -360,6 +399,7 @@ test.describe("playground links", () => {
test("loads code from the hash", async ({ page }) => {
const code = "let hashValue = 1;";
await page.goto(`/playground#code=${encodeURIComponent(encodeCode(code))}`);
await activatePlayground(page);

await expect(page.getByTestId("editor").getByRole("textbox")).toContainText(
code,
Expand Down Expand Up @@ -406,6 +446,7 @@ test.describe("playground links", () => {
"files.main.ts": encodeCode('import { nested } from "./src/component";'),
});
await page.goto(`/playground#${hash}`);
await activatePlayground(page);

await expect(
page.getByRole("button", { name: "component.ts", exact: true }),
Expand Down Expand Up @@ -477,6 +518,7 @@ test.describe("playground links", () => {
),
});
await page.goto(`/playground#${hash}`);
await activatePlayground(page);

await expect(
page.getByTestId("biome-output").getByRole("textbox"),
Expand All @@ -489,6 +531,7 @@ test.describe("playground layout", () => {
await page.goto(
`/playground?prettier=true#code=${encodeURIComponent(encodeCode("let a=5"))}`,
);
await activatePlayground(page);
const output = page.getByTestId("biome-output").getByRole("textbox");
const comparePrettier = page.getByLabel("Compare Prettier");
await expect(output).toContainText("let a = 5;");
Expand Down Expand Up @@ -517,6 +560,7 @@ test.describe("playground layout", () => {
await page.goto(
`/playground?enabledLinting=false&fix=safeFixes#code=${encodeURIComponent(encodeCode("let a=5"))}`,
);
await activatePlayground(page);

await expect(
page.getByTestId("biome-output").getByRole("textbox"),
Expand Down Expand Up @@ -594,6 +638,7 @@ test.describe("playground layout", () => {
await expect(page.getByLabel("Language", { exact: true })).toBeDisabled();

await page.reload();
await activatePlayground(page);
await page.getByRole("button", { name: "main.tsx", exact: true }).click();
await page.getByRole("button", { name: "Safe", exact: true }).click();
await page.getByLabel("Format", { exact: true }).uncheck();
Expand Down Expand Up @@ -848,6 +893,7 @@ test.describe("playground layout", () => {
localStorage.setItem("playground:gritql-matches-ratio", String(size));
}, size);
await page.goto(`/playground?code=${gritCode}`);
await activatePlayground(page);
await page.getByRole("button", { name: "GritQL search" }).click();
await page
.locator(".gritql-panel .cm-content")
Expand Down Expand Up @@ -884,6 +930,7 @@ test.describe("playground layout", () => {
await page.goto(
`/playground?prettier=true#code=${encodeURIComponent(encodeCode(code))}`,
);
await activatePlayground(page);
const biome = page.getByTestId("biome-output");
const prettier = page.getByTestId("prettier-output");
await expect(prettier.getByRole("textbox")).toContainText("let a59 = 59;");
Expand Down Expand Up @@ -950,6 +997,7 @@ test.describe("playground GritQL search", () => {
await page.goto(
`/playground?files.${file}=${encodeURIComponent(encodeCode(code))}`,
);
await activatePlayground(page);
await toggleView(page, "GritQL search");
await page.locator(".gritql-select").selectOption({ label: language });
await page.locator(".gritql-panel .cm-content").fill(query);
Expand Down
Loading