Back to Repositories

Testing Hot Reload Functionality in Gradio Development Mode

This test suite validates the hot reload functionality in Gradio’s development mode, specifically testing how the application handles dynamic content updates and UI changes. It ensures seamless page reloading while maintaining application state and user interactions.

Test Coverage Overview

The test suite provides comprehensive coverage of Gradio’s development mode reload capabilities.

Key areas tested include:
  • Application initialization and server startup
  • Dynamic file modifications during runtime
  • UI component updates and state preservation
  • User interaction handling post-reload

Implementation Analysis

The testing approach utilizes Playwright for browser automation and interaction simulation. The implementation follows a structured pattern with proper setup and teardown phases.

Technical implementation features:
  • Process management for server control
  • File system operations for dynamic content updates
  • Asynchronous page navigation and state verification
  • Timeout handling for reload operations

Technical Details

Testing infrastructure includes:
  • Playwright test framework for browser automation
  • Node’s child_process for server management
  • Custom utility functions for process handling
  • Environment configuration for Python unbuffered output
  • File system operations for test file management

Best Practices Demonstrated

The test implementation showcases several testing best practices and patterns.

Notable practices include:
  • Proper test isolation with beforeAll/afterAll hooks
  • Resource cleanup and process management
  • Error handling with try/finally blocks
  • Explicit timeouts for async operations
  • Clear separation of setup, execution, and verification phases

gradio-app/gradio

js/spa/test/hello_world.reload.spec.ts

            
import { test, expect } from "@playwright/test";
import { spawnSync } from "node:child_process";
import { launch_app_background, kill_process } from "./utils";
import { join } from "path";

let _process;

test.beforeAll(() => {
	const demo = `
import gradio as gr
    
def greet(name):
    return "Hello " + name + "!"

demo = gr.Interface(fn=greet, inputs="text", outputs="text")

if __name__ == "__main__":
    demo.launch()
`;
	// write contents of demo to a local 'run.py' file
	spawnSync(`echo '${demo}' > ${join(process.cwd(), "run.py")}`, {
		shell: true,
		stdio: "pipe",
		env: {
			...process.env,
			PYTHONUNBUFFERED: "true"
		}
	});
});

test.afterAll(() => {
	if (_process) kill_process(_process);
	spawnSync(`rm  ${join(process.cwd(), "run.py")}`, {
		shell: true,
		stdio: "pipe",
		env: {
			...process.env,
			PYTHONUNBUFFERED: "true"
		}
	});
});

test("gradio dev mode correctly reloads the page", async ({ page }) => {
	test.setTimeout(20 * 1000);

	try {
		const { _process: server_process, port: port } =
			await launch_app_background(
				`gradio ${join(process.cwd(), "run.py")}`,
				process.cwd()
			);
		_process = server_process;
		console.log("Connected to port", port);
		const demo = `
import gradio as gr

def greet(name):
    return "Hello " + name + "!"

demo = gr.Interface(fn=greet, inputs=gr.Textbox(label="x"), outputs=gr.Textbox(label="foo"))

if __name__ == "__main__":
    demo.launch()
    `;
		// write contents of demo to a local 'run.py' file
		await page.goto(`http://localhost:${port}`);
		await page.waitForTimeout(2000);
		spawnSync(`echo '${demo}' > ${join(process.cwd(), "run.py")}`, {
			shell: true,
			stdio: "pipe",
			env: {
				...process.env,
				PYTHONUNBUFFERED: "true"
			}
		});

		await page.getByLabel("x").fill("Maria");
		await page.getByRole("button", { name: "Submit" }).click();

		await expect(page.getByLabel("foo")).toHaveValue("Hello Maria!");
	} finally {
		if (_process) kill_process(_process);
	}
});