diff --git a/.changeset/whole-kids-nail.md b/.changeset/whole-kids-nail.md new file mode 100644 index 0000000000000..dfa9c05b493d8 --- /dev/null +++ b/.changeset/whole-kids-nail.md @@ -0,0 +1,7 @@ +--- +"@gradio/imageeditor": patch +"@self/storybook": patch +"gradio": patch +--- + +feat:Add more ImageEditor js tests diff --git a/.github/workflows/storybook-build.yml b/.github/workflows/storybook-build.yml index 00a2415d119de..5482df8c7e7d7 100644 --- a/.github/workflows/storybook-build.yml +++ b/.github/workflows/storybook-build.yml @@ -53,15 +53,18 @@ jobs: uses: "gradio-app/gradio/.github/actions/install-all-deps@main" with: python_version: "3.10" - skip_build: "true" + skip_build: "false" - name: build client run: pnpm --filter @gradio/client build - name: generate theme.css run: | . venv/bin/activate python scripts/generate_theme.py --outfile js/storybook/theme.css + - run: pnpm exec playwright install chromium - name: build storybook - run: pnpm build-storybook --quiet + run: | + . venv/bin/activate + pnpm build-storybook --quiet - name: upload storybook uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index db77142d47a78..f43d3f54389e0 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ demo/unload_event_test/output_log.txt demo/stream_video_out/output_*.ts demo/stream_video_out/output_*.mp4 demo/stream_audio_out/*.mp3 +#demo/image_editor_story/*.png # Etc .idea/* @@ -93,6 +94,7 @@ client/js/test.js storybook-static build-storybook.log js/storybook/theme.css +#js/storybook/public/output-image.png # playwright .config/playwright/.cache \ No newline at end of file diff --git a/demo/image_editor_canvas_size/run.ipynb b/demo/image_editor_canvas_size/run.ipynb index 43f38d527fa2b..cb3f494079d91 100644 --- a/demo/image_editor_canvas_size/run.ipynb +++ b/demo/image_editor_canvas_size/run.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: image_editor_canvas_size"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.ImageEditor(label=\"Default Canvas. Not fixed\", elem_id=\"default\")\n", " with gr.Column():\n", " custom_canvas = gr.ImageEditor(label=\"Custom Canvas, not fixed\", canvas_size=(300, 300),\n", " elem_id=\"small\")\n", " with gr.Column():\n", " custom_canvas_fixed = gr.ImageEditor(label=\"Custom Canvas,fixed\", canvas_size=(500, 500), fixed_canvas=True,\n", " elem_id=\"fixed\")\n", " with gr.Column():\n", " width = gr.Number(label=\"Width\")\n", " height = gr.Number(label=\"Height\")\n", "\n", " image.change(lambda x: x[\"composite\"].shape, outputs=[height, width], inputs=image)\n", " custom_canvas.change(lambda x: x[\"composite\"].shape, outputs=[height, width], inputs=custom_canvas)\n", " custom_canvas_fixed.change(lambda x: x[\"composite\"].shape, outputs=[height, width], inputs=custom_canvas_fixed)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: image_editor_canvas_size"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.ImageEditor(label=\"Default Canvas. Not fixed\", elem_id=\"default\")\n", " get_image = gr.Button(\"Get Default\")\n", " with gr.Column():\n", " custom_canvas = gr.ImageEditor(label=\"Custom Canvas, not fixed\", canvas_size=(300, 300),\n", " elem_id=\"small\")\n", " get_small = gr.Button(\"Get Small\")\n", " with gr.Column():\n", " custom_canvas_fixed = gr.ImageEditor(label=\"Custom Canvas,fixed\", canvas_size=(500, 500), fixed_canvas=True,\n", " elem_id=\"fixed\")\n", " get_fixed = gr.Button(\"Get Fixed\")\n", " with gr.Column():\n", " width = gr.Number(label=\"Width\")\n", " height = gr.Number(label=\"Height\")\n", "\n", " get_image.click(lambda x: x[\"composite\"].shape, outputs=[height, width], inputs=image)\n", " get_small.click(lambda x: x[\"composite\"].shape, outputs=[height, width], inputs=custom_canvas)\n", " get_fixed.click(lambda x: x[\"composite\"].shape, outputs=[height, width], inputs=custom_canvas_fixed)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/image_editor_canvas_size/run.py b/demo/image_editor_canvas_size/run.py index 8aec553ade3d7..5cb1929336c72 100644 --- a/demo/image_editor_canvas_size/run.py +++ b/demo/image_editor_canvas_size/run.py @@ -4,19 +4,22 @@ with gr.Row(): with gr.Column(): image = gr.ImageEditor(label="Default Canvas. Not fixed", elem_id="default") + get_image = gr.Button("Get Default") with gr.Column(): custom_canvas = gr.ImageEditor(label="Custom Canvas, not fixed", canvas_size=(300, 300), elem_id="small") + get_small = gr.Button("Get Small") with gr.Column(): custom_canvas_fixed = gr.ImageEditor(label="Custom Canvas,fixed", canvas_size=(500, 500), fixed_canvas=True, elem_id="fixed") + get_fixed = gr.Button("Get Fixed") with gr.Column(): width = gr.Number(label="Width") height = gr.Number(label="Height") - image.change(lambda x: x["composite"].shape, outputs=[height, width], inputs=image) - custom_canvas.change(lambda x: x["composite"].shape, outputs=[height, width], inputs=custom_canvas) - custom_canvas_fixed.change(lambda x: x["composite"].shape, outputs=[height, width], inputs=custom_canvas_fixed) + get_image.click(lambda x: x["composite"].shape, outputs=[height, width], inputs=image) + get_small.click(lambda x: x["composite"].shape, outputs=[height, width], inputs=custom_canvas) + get_fixed.click(lambda x: x["composite"].shape, outputs=[height, width], inputs=custom_canvas_fixed) if __name__ == "__main__": demo.launch() \ No newline at end of file diff --git a/demo/image_editor_story/run.ipynb b/demo/image_editor_story/run.ipynb new file mode 100644 index 0000000000000..4131d267691fa --- /dev/null +++ b/demo/image_editor_story/run.ipynb @@ -0,0 +1 @@ +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: image_editor_story"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from pathlib import Path\n", "import subprocess\n", "\n", "\n", "def predict(im):\n", " path = str(Path(__file__).parent / \"output-image.png\")\n", " with open(path, \"wb\") as f:\n", " f.write(Path(im[\"composite\"]).read_bytes())\n", " print(\"Writing to \", path)\n", " return path\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " im = gr.ImageEditor(interactive=True, elem_id=\"image_editor\",\n", " canvas_size=(800, 600),\n", " brush=gr.Brush(colors=[\"#ff0000\", \"#00ff00\", \"#0000ff\"]),\n", " type=\"filepath\",\n", " value={\"background\": \"https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg\",\n", " \"layers\": [],\n", " \"composite\": None})\n", " get = gr.Button(\"Get\")\n", "\n", " with gr.Column():\n", " output = gr.Image(value=None, elem_id=\"output\")\n", "\n", " get.click(predict, inputs=im, outputs=output)\n", "\n", "if __name__ == \"__main__\":\n", " app, _, _ = demo.launch(prevent_thread_lock=True)\n", " subprocess.call([\"node\", \"js/storybook/ie_automation.js\"])\n", " demo.close()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/image_editor_story/run.py b/demo/image_editor_story/run.py new file mode 100644 index 0000000000000..adfbe52b85b70 --- /dev/null +++ b/demo/image_editor_story/run.py @@ -0,0 +1,34 @@ +import gradio as gr +from pathlib import Path +import subprocess + + +def predict(im): + path = str(Path(__file__).parent / "output-image.png") + with open(path, "wb") as f: + f.write(Path(im["composite"]).read_bytes()) + print("Writing to ", path) + return path + + +with gr.Blocks() as demo: + with gr.Row(): + with gr.Column(): + im = gr.ImageEditor(interactive=True, elem_id="image_editor", + canvas_size=(800, 600), + brush=gr.Brush(colors=["#ff0000", "#00ff00", "#0000ff"]), + type="filepath", + value={"background": "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg", + "layers": [], + "composite": None}) + get = gr.Button("Get") + + with gr.Column(): + output = gr.Image(value=None, elem_id="output") + + get.click(predict, inputs=im, outputs=output) + +if __name__ == "__main__": + app, _, _ = demo.launch(prevent_thread_lock=True) + subprocess.call(["node", "js/storybook/ie_automation.js"]) + demo.close() diff --git a/js/imageeditor/ImageEditor.stories.svelte b/js/imageeditor/ImageEditor.stories.svelte index bbb51e9f7f08d..e69f38ae66d92 100644 --- a/js/imageeditor/ImageEditor.stories.svelte +++ b/js/imageeditor/ImageEditor.stories.svelte @@ -55,157 +55,6 @@ }} /> - - + + diff --git a/js/imageeditor/shared/layers/Layers.svelte b/js/imageeditor/shared/layers/Layers.svelte index 25d987728edf7..0f1d2dd18881d 100644 --- a/js/imageeditor/shared/layers/Layers.svelte +++ b/js/imageeditor/shared/layers/Layers.svelte @@ -145,6 +145,7 @@
  • { await page.locator("#default >> .upload-container > button").click(); const uploader = page.locator("#default >> input[type=file]"); await uploader.setInputFiles(["./test/files/bike.jpeg"]); + await page.waitForTimeout(500); + await page.getByRole("button", { name: "Get Default" }).click(); await expect(page.getByLabel("Width")).toHaveValue("1024"); await expect(page.getByLabel("Height")).toHaveValue("769"); }); -test.skip("Image Editor 300 x 300 canvas resizes to match uploaded image", async ({ +test("Image Editor 300 x 300 canvas resizes to match uploaded image", async ({ page }) => { await page.locator("#small >> .upload-container > button").click(); const uploader = page.locator("#small >> input[type=file]"); await uploader.setInputFiles(["./test/files/bike.jpeg"]); + await page.waitForTimeout(500); + await page.getByRole("button", { name: "Get Small" }).click(); await expect(page.getByLabel("Width")).toHaveValue("1024"); await expect(page.getByLabel("Height")).toHaveValue("769"); }); -test.skip("Image Editor 300 x 300 canvas maintains size while being drawn upon", async ({ +test("Image Editor 300 x 300 canvas maintains size while being drawn upon", async ({ page }) => { await page.locator("#small").getByLabel("Draw button").click(); await page.locator("#small canvas").click({ position: { x: 15, y: 18 } }); + await page.waitForTimeout(500); + await page.getByRole("button", { name: "Get Small" }).click(); await expect(page.getByLabel("Width")).toHaveValue("300"); await expect(page.getByLabel("Height")).toHaveValue("300"); await page.locator("#small canvas").click({ position: { x: 10, y: 12 } }); + await page.waitForTimeout(500); + await page.getByRole("button", { name: "Get Small" }).click(); await expect(page.getByLabel("Width")).toHaveValue("300"); await expect(page.getByLabel("Height")).toHaveValue("300"); }); -test.skip("Image Editor reshapes image to fit fixed 500 x 500 canvas", async ({ +test("Image Editor reshapes image to fit fixed 500 x 500 canvas", async ({ page }) => { await page.locator("#small >> .upload-container > button").click(); const uploader = page.locator("#fixed >> input[type=file]"); await uploader.setInputFiles(["./test/files/bike.jpeg"]); + await page.waitForTimeout(500); + await page.getByRole("button", { name: "Get Fixed" }).click(); await expect(page.getByLabel("Width")).toHaveValue("500"); await expect(page.getByLabel("Height")).toHaveValue("500"); diff --git a/js/spa/test/image_editor_events.spec.ts b/js/spa/test/image_editor_events.spec.ts index edfb4bb6fe7b9..bf1a35097e4e9 100644 --- a/js/spa/test/image_editor_events.spec.ts +++ b/js/spa/test/image_editor_events.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, drag_and_drop_file } from "@self/tootils"; +import { test, expect } from "@self/tootils"; test("upload events work as expected", async ({ page }) => { await page.getByLabel("Upload button").first().click(); diff --git a/js/storybook/ie_automation.js b/js/storybook/ie_automation.js new file mode 100644 index 0000000000000..d92284cb04bfc --- /dev/null +++ b/js/storybook/ie_automation.js @@ -0,0 +1,52 @@ +import { chromium } from "playwright"; + +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await browser.newPage(); + + await page.goto("http://localhost:7860"); + + await page.getByLabel("Show Layers").click(); + await page.waitForTimeout(500); + + await page.getByLabel("layer-1").click(); + await page.waitForTimeout(500); + + const draw_button = page.getByLabel("Draw button").first(); + await draw_button.click(); + + await page.mouse.move(300, 100); + await page.mouse.down(); + await page.mouse.move(300, 300); + await page.waitForTimeout(300); + + await page.mouse.move(100, 100); + await page.mouse.down(); + await page.waitForTimeout(500); + await page.mouse.up(); + + await draw_button.click(); + await page.waitForTimeout(300); + const color = page.locator("menu > button").nth(1); + await color.click(); + await page.mouse.move(300, 100); + await page.mouse.down(); + await page.mouse.move(100, 100); + + await page.getByLabel("Transform button").click(); + await page.waitForTimeout(300); + const right_handle = page.locator(".handle.corner.r"); + const rectangle = await right_handle.boundingBox(); + + //@ts-ignore + await page.mouse.move(rectangle.x, rectangle.y); + await page.mouse.down(); + //@ts-ignore + await page.mouse.move(rectangle.x - 100, rectangle.y, { steps: 25 }); + await page.mouse.up(); + await page.waitForTimeout(500); + + await page.getByRole("button", { name: "Get" }).click(); + await page.waitForTimeout(7000); + await browser.close(); +})(); diff --git a/package.json b/package.json index 6c5f520a6efe8..70942be9fa4fd 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test:browser:verbose": "pnpm test:browser", "test:browser:dev": "pnpm --filter @self/spa test:browser:dev", "storybook": "storybook dev -p 6006 --config-dir js/storybook", - "build-storybook": "storybook build --config-dir js/storybook --webpack-stats-json", + "build-storybook": "python demo/image_editor_story/run.py && cp demo/image_editor_story/output-image.png js/storybook/public && storybook build --config-dir js/storybook --webpack-stats-json", "ci:version": "changeset version && pnpm i --lockfile-only && node ./.changeset/fix_changelogs.cjs", "ci:publish": "pnpm changeset tag && git push origin --tags && pnpm publish --no-git-checks --access public -r --filter=@gradio/*", "chromatic": "chromatic",