Skip to content

Fix skulpt support for output panels attribute #1158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 6, 2025
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
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@raspberrypifoundation/editor-ui",
"version": "0.28.13",
"version": "0.28.14",
"private": true,
"dependencies": {
"@apollo/client": "^3.7.8",
Expand Down
7 changes: 5 additions & 2 deletions src/components/Editor/Runners/PythonRunner/PythonRunner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -93,7 +93,10 @@ const PythonRunner = () => {
return (
<>
<PyodideRunner active={activeRunner === "pyodide"} />
<SkulptRunner active={activeRunner === "skulpt"} />
<SkulptRunner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if it's worth adding a simple test to check the outputPanels prop is getting added? just for a bit of regression coverage (and to help avoid it getting missed in the future)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Dan, I've added a couple of tests to check this (although in a bit of a roundabout way, as I think React Testing Library doesn't let you test which props are on components, just what's in the DOM annoyingly)

active={activeRunner === "skulpt"}
outputPanels={outputPanels}
/>
</>
);
};
Expand Down
227 changes: 134 additions & 93 deletions src/components/Editor/Runners/PythonRunner/PythonRunner.test.js
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -56,110 +56,151 @@ const updateRunner = ({
store.dispatch(setSenseHatAlwaysEnabled(senseHatAlwaysEnabled));
});
};
describe("PythonRunner with default props", () => {
beforeEach(() => {
window.crossOriginIsolated = true;
render(
<Provider store={store}>
<PythonRunner />
</Provider>,
);
updateRunner({ code: "print('some loaded code')" });
});

beforeEach(() => {
window.crossOriginIsolated = true;
render(
<Provider store={store}>
<PythonRunner />
</Provider>,
);
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(
<Provider store={store}>
<PythonRunner outputPanels={["visual"]} />
</Provider>,
);
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();
});
});
Loading