diff --git a/CHANGELOG.md b/CHANGELOG.md index ff853b266..ec1db3ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +## [0.28.14] - 2025-01-06 + +### Fixed + +- Support for the `outputPanels` attribute in the `SkulptRunner` (#1158) + ## [0.28.13] - 2024-12-18 ### Changed @@ -1019,7 +1027,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Events in Web Component indicating whether Mission Zero criteria have been met (#113) -[unreleased]: https://github.com/RaspberryPiFoundation/editor-ui/compare/v0.28.13...HEAD +[unreleased]: https://github.com/RaspberryPiFoundation/editor-ui/compare/v0.28.14...HEAD +[0.28.14]: https://github.com/RaspberryPiFoundation/editor-ui/releases/tag/v0.28.14 [0.28.13]: https://github.com/RaspberryPiFoundation/editor-ui/releases/tag/v0.28.13 [0.28.12]: https://github.com/RaspberryPiFoundation/editor-ui/releases/tag/v0.28.12 [0.28.11]: https://github.com/RaspberryPiFoundation/editor-ui/releases/tag/v0.28.11 diff --git a/package.json b/package.json index 2d05fb030..bbda89f88 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@raspberrypifoundation/editor-ui", - "version": "0.28.13", + "version": "0.28.14", "private": true, "dependencies": { "@apollo/client": "^3.7.8", diff --git a/src/components/Editor/Runners/PythonRunner/PythonRunner.jsx b/src/components/Editor/Runners/PythonRunner/PythonRunner.jsx index cc34e65cb..48babc88b 100644 --- a/src/components/Editor/Runners/PythonRunner/PythonRunner.jsx +++ b/src/components/Editor/Runners/PythonRunner/PythonRunner.jsx @@ -15,7 +15,7 @@ const SKULPT_ONLY_MODULES = [ "turtle", ]; -const PythonRunner = () => { +const PythonRunner = ({ outputPanels = ["text", "visual"] }) => { const dispatch = useDispatch(); const project = useSelector((state) => state.editor.project); @@ -93,7 +93,10 @@ const PythonRunner = () => { return ( <> - + ); }; diff --git a/src/components/Editor/Runners/PythonRunner/PythonRunner.test.js b/src/components/Editor/Runners/PythonRunner/PythonRunner.test.js index 7a93dd81b..b92f9cf3f 100644 --- a/src/components/Editor/Runners/PythonRunner/PythonRunner.test.js +++ b/src/components/Editor/Runners/PythonRunner/PythonRunner.test.js @@ -1,4 +1,4 @@ -import { render } from "@testing-library/react"; +import { render, within } from "@testing-library/react"; import { Provider } from "react-redux"; import { act } from "react-dom/test-utils"; import PythonRunner from "./PythonRunner"; @@ -56,110 +56,151 @@ const updateRunner = ({ store.dispatch(setSenseHatAlwaysEnabled(senseHatAlwaysEnabled)); }); }; +describe("PythonRunner with default props", () => { + beforeEach(() => { + window.crossOriginIsolated = true; + render( + + + , + ); + updateRunner({ code: "print('some loaded code')" }); + }); -beforeEach(() => { - window.crossOriginIsolated = true; - render( - - - , - ); - updateRunner({ code: "print('some loaded code')" }); -}); + test("Renders with Pyodide runner initially", () => { + updateRunner({}); + expect( + document.querySelector(".skulptrunner--active"), + ).not.toBeInTheDocument(); + expect( + document.querySelector(".pyodiderunner--active"), + ).toBeInTheDocument(); + }); -test("Renders with Pyodide runner initially", () => { - updateRunner({}); - expect( - document.querySelector(".skulptrunner--active"), - ).not.toBeInTheDocument(); - expect(document.querySelector(".pyodiderunner--active")).toBeInTheDocument(); -}); + test("Uses pyodide when no skulpt-only modules are imported", () => { + updateRunner({ code: "import math" }); + expect( + document.querySelector(".skulptrunner--active"), + ).not.toBeInTheDocument(); + expect( + document.querySelector(".pyodiderunner--active"), + ).toBeInTheDocument(); + }); -test("Uses pyodide when no skulpt-only modules are imported", () => { - updateRunner({ code: "import math" }); - expect( - document.querySelector(".skulptrunner--active"), - ).not.toBeInTheDocument(); - expect(document.querySelector(".pyodiderunner--active")).toBeInTheDocument(); -}); + test("Uses skulpt when skulpt-only modules are imported", () => { + updateRunner({ code: "import p5" }); + expect( + document.querySelector(".pyodiderunner--active"), + ).not.toBeInTheDocument(); + expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); + }); -test("Uses skulpt when skulpt-only modules are imported", () => { - updateRunner({ code: "import p5" }); - expect( - document.querySelector(".pyodiderunner--active"), - ).not.toBeInTheDocument(); - expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); -}); + test("Uses skulpt when senseHatAlwaysEnabled is true", () => { + updateRunner({ code: "import math", senseHatAlwaysEnabled: true }); + expect( + document.querySelector(".pyodiderunner--active"), + ).not.toBeInTheDocument(); + expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); + }); -test("Uses skulpt when senseHatAlwaysEnabled is true", () => { - updateRunner({ code: "import math", senseHatAlwaysEnabled: true }); - expect( - document.querySelector(".pyodiderunner--active"), - ).not.toBeInTheDocument(); - expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); -}); + test("Uses skulpt if not cross origin isolated", () => { + window.crossOriginIsolated = false; + updateRunner({ code: "import math" }); + expect( + document.querySelector(".pyodiderunner--active"), + ).not.toBeInTheDocument(); + expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); + }); -test("Uses skulpt if not cross origin isolated", () => { - window.crossOriginIsolated = false; - updateRunner({ code: "import math" }); - expect( - document.querySelector(".pyodiderunner--active"), - ).not.toBeInTheDocument(); - expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); -}); + test("Switches runners if the import has a from clause", () => { + updateRunner({ code: "from p5 import *" }); + expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); + expect( + document.querySelector(".pyodiderunner--active"), + ).not.toBeInTheDocument(); + }); -test("Switches runners if the import has a from clause", () => { - updateRunner({ code: "from p5 import *" }); - expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".pyodiderunner--active"), - ).not.toBeInTheDocument(); -}); + test("Switches runners if the import is indented", () => { + updateRunner({ code: " import p5" }); + expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); + expect( + document.querySelector(".pyodiderunner--active"), + ).not.toBeInTheDocument(); + }); -test("Switches runners if the import is indented", () => { - updateRunner({ code: " import p5" }); - expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".pyodiderunner--active"), - ).not.toBeInTheDocument(); -}); + test("Uses skulpt if the py5 magic comment is used", () => { + updateRunner({ code: "# input.comment.py5" }); + expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); + expect( + document.querySelector(".pyodiderunner--active"), + ).not.toBeInTheDocument(); + }); -test("Uses skulpt if the py5 magic comment is used", () => { - updateRunner({ code: "# input.comment.py5" }); - expect(document.querySelector(".skulptrunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".pyodiderunner--active"), - ).not.toBeInTheDocument(); -}); + test("Does not switch runners while the code is running", () => { + updateRunner({ code: "import p5", codeRunTriggered: true }); + expect( + document.querySelector(".pyodiderunner--active"), + ).toBeInTheDocument(); + expect( + document.querySelector(".skulptrunner--active"), + ).not.toBeInTheDocument(); + }); -test("Does not switch runners while the code is running", () => { - updateRunner({ code: "import p5", codeRunTriggered: true }); - expect(document.querySelector(".pyodiderunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".skulptrunner--active"), - ).not.toBeInTheDocument(); -}); + test("Does not switch runners if the import is in a comment", () => { + updateRunner({ code: "# import p5" }); + expect( + document.querySelector(".pyodiderunner--active"), + ).toBeInTheDocument(); + expect( + document.querySelector(".skulptrunner--active"), + ).not.toBeInTheDocument(); + }); -test("Does not switch runners if the import is in a comment", () => { - updateRunner({ code: "# import p5" }); - expect(document.querySelector(".pyodiderunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".skulptrunner--active"), - ).not.toBeInTheDocument(); -}); + test("Does not switch runners if the import is in a string", () => { + updateRunner({ code: 'print("import p5")' }); + expect( + document.querySelector(".pyodiderunner--active"), + ).toBeInTheDocument(); + expect( + document.querySelector(".skulptrunner--active"), + ).not.toBeInTheDocument(); + }); -test("Does not switch runners if the import is in a string", () => { - updateRunner({ code: 'print("import p5")' }); - expect(document.querySelector(".pyodiderunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".skulptrunner--active"), - ).not.toBeInTheDocument(); + test("Does not switch runners if the import is in a multiline string", () => { + updateRunner({ code: '"""\nimport p5\n"""' }); + expect( + document.querySelector(".pyodiderunner--active"), + ).toBeInTheDocument(); + expect( + document.querySelector(".skulptrunner--active"), + ).not.toBeInTheDocument(); + }); + + test("Renders the text output in the skulpt runner", () => { + updateRunner({ code: "import p5" }); + const skulptRunner = document.querySelector(".skulptrunner"); + expect( + within(skulptRunner).queryByText("output.textOutput"), + ).toBeInTheDocument(); + }); }); -test("Does not switch runners if the import is in a multiline string", () => { - updateRunner({ code: '"""\nimport p5\n"""' }); - expect(document.querySelector(".pyodiderunner--active")).toBeInTheDocument(); - expect( - document.querySelector(".skulptrunner--active"), - ).not.toBeInTheDocument(); +describe("PythonRunner with output panels set to visual only", () => { + beforeEach(() => { + window.crossOriginIsolated = true; + render( + + + , + ); + updateRunner({ code: "print('some loaded code')" }); + }); + + test("Does not render text output in the skulpt runner", () => { + updateRunner({ code: "import p5" }); + const skulptRunner = document.querySelector(".skulptrunner"); + expect( + within(skulptRunner).queryByText("output.textOutput"), + ).not.toBeInTheDocument(); + }); });