Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
aa09f15
Create draft PR for #936
create-issue-branch[bot] Feb 27, 2024
8814cae
default to pyodide and switch to skulpt if needed
loiswells97 Feb 27, 2024
1f0e5d0
attempt to parse imports in the runnerfactory
loiswells97 Feb 27, 2024
12bdce4
refactoring
loiswells97 Feb 28, 2024
f0ba6c3
tidying for linter
loiswells97 Feb 28, 2024
825961e
tidying to clarify approach in pr
loiswells97 Feb 28, 2024
e4354ed
refactoring to fix things
loiswells97 Feb 28, 2024
534871c
tidying
loiswells97 Feb 28, 2024
fb17e4d
tidying
loiswells97 Feb 29, 2024
7a3a74c
Merge branch 'main' into issues/936-Spike_Investigate_using_pyodide_d…
loiswells97 Feb 29, 2024
e69126c
Merge branch 'main' into issues/936-Spike_Investigate_using_pyodide_d…
loiswells97 Feb 29, 2024
edabb66
Merge branch 'main' into issues/936-Spike_Investigate_using_pyodide_d…
loiswells97 Feb 29, 2024
ff0587f
fixing shikm assets
loiswells97 Apr 4, 2024
73ae099
Merge branch 'main' into issues/936-Spike_Investigate_using_pyodide_d…
loiswells97 Apr 4, 2024
beaad38
initial attempt at explaining incompatible modules
loiswells97 Apr 4, 2024
901cd1d
Fixing snapshot and skulptrunner tests
loiswells97 Apr 4, 2024
88ce726
fixing pyodiderunner tests
loiswells97 Apr 4, 2024
ea622af
fixing py5 imported mode
loiswells97 Apr 4, 2024
95b85ab
fixing cypress tests
loiswells97 Apr 4, 2024
ef868e0
updating snapshot
loiswells97 Apr 4, 2024
f9d8b67
fixing linting warning
loiswells97 Apr 5, 2024
610f6ad
updating changelog
loiswells97 Apr 5, 2024
e399def
fixing the input function
loiswells97 Apr 5, 2024
797a559
trying to fix cypress
loiswells97 Apr 5, 2024
691f9fb
patching pygal tooltip formatter
loiswells97 Apr 8, 2024
b1683fe
couple of regex patches to eliminate some edge cases
loiswells97 Apr 8, 2024
f76ea51
tidying
loiswells97 Apr 8, 2024
fdc914d
moving pyodiderunner back for now to avoid conflicts
loiswells97 Apr 8, 2024
52c4067
Merge branch 'main' into issues/936-Spike_Investigate_using_pyodide_d…
loiswells97 Apr 8, 2024
1b82f42
tidying
loiswells97 Apr 8, 2024
68d85dc
changing and fixing tests
loiswells97 Apr 9, 2024
41594e6
fixing more tests
loiswells97 Apr 9, 2024
a571b06
fix mission zero tests
loiswells97 Apr 9, 2024
ce79754
testing pythonrunner switching which runner is active
loiswells97 Apr 9, 2024
70d1eea
updating snapshot
loiswells97 Apr 9, 2024
e53a055
tweaking and testing skulpt and pyodide runner visibilities
loiswells97 Apr 9, 2024
868ab31
tweaking incompatibility error message explanation
loiswells97 Apr 9, 2024
d2fcfc3
fix one of the mission zero cypress tests
loiswells97 Apr 10, 2024
a73fcad
update error explanation copy
loiswells97 Apr 10, 2024
bdb30a1
switching turtle to skulpt and forcing skulpt if sense hat always ena…
loiswells97 Apr 10, 2024
5d43c1a
update error explanation to include turtle
loiswells97 Apr 10, 2024
8e8bdc9
updating snapshot
loiswells97 Apr 10, 2024
e45e8e3
switching mission zero cypress port
loiswells97 Apr 10, 2024
d9b38f3
Merge branch 'pyodide-migration' into issues/936-Spike_Investigate_us…
sra405 Apr 29, 2024
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- Support to enable embedding iframes in HTML projects from in-house domains (#985)
- Unit tests for `pyodide` runner (#976)
- Dynamic switching between `pyodide` and `skulpt` based on user imports (#937)

### Changed

- Runner defaults to `pyodide` (#937)

## [0.22.2] - 2024-03-18

Expand Down
26 changes: 16 additions & 10 deletions cypress/e2e/missionZero-wc.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ it("loads the sense hat library", () => {
});

it("sets initial criteria correctly", () => {
cy.get("editor-wc")
.shadow()
.find("div[class=cm-content]")
.invoke("text", "from sense_hat import SenseHat");
cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke("text", "");
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.get("#results").should(
"contain",
Expand Down Expand Up @@ -100,7 +97,10 @@ it("resets criteria correctly", () => {
);
cy.get("editor-wc").shadow().find(".btn--run").contains("Run").click();
cy.get("#results").should("contain", '"readPressure":true');
cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke("text", "");
cy.get("editor-wc")
.shadow()
.find("div[class=cm-content]")
.invoke("text", "from sense_hat import SenseHat");
cy.get("editor-wc").shadow().find(".btn--run").contains("Run").click();
cy.get("#results").should(
"contain",
Expand Down Expand Up @@ -139,7 +139,13 @@ it("picks up calls to input()", () => {
.find("div[class=cm-content]")
.invoke("text", "input()");
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.get("editor-wc").shadow().contains("Text output").click();
cy.get("editor-wc")
.shadow()
.find(
"div[class='pythonrunner-container skulptrunner skulptrunner--active']",
)
.contains("Text output")
.click();
cy.get("editor-wc")
.shadow()
.find("span[contenteditable=true]")
Expand Down Expand Up @@ -174,7 +180,7 @@ it("does not return null duration if no change in focus", () => {
.find("div[class=cm-content]")
.invoke(
"text",
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.send_message("a")',
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.show_message("a")',
);
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.get("#results").should("not.contain", '"duration":null');
Expand All @@ -186,7 +192,7 @@ it("does not return null duration if focus changed before code run", () => {
.find("div[class=cm-content]")
.invoke(
"text",
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.send_message("a")',
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.show_message("a")',
);
cy.window().blur();
cy.window().focus();
Expand All @@ -200,7 +206,7 @@ it("returns duration of null if focus is lost", () => {
.find("div[class=cm-content]")
.invoke(
"text",
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.send_message("a")',
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.show_message("a")',
);
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.window().blur();
Expand All @@ -214,7 +220,7 @@ it("does not return duration of null if code rerun after focus lost", () => {
.find("div[class=cm-content]")
.invoke(
"text",
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.send_message("a")',
'from sense_hat import SenseHat\nsense = SenseHat()\nsense.show_message("a")',
);
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.window().blur();
Expand Down
5 changes: 4 additions & 1 deletion cypress/e2e/spec-wc.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ it("does not render astro pi component if sense hat unimported", () => {
.find("div[class=cm-content]")
.invoke("text", "import sense_hat");
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke("text", "");
cy.get("editor-wc")
.shadow()
.find("div[class=cm-content]")
.invoke("text", "import p5");
cy.get("editor-wc").shadow().find(".btn--run").click();
cy.get("editor-wc").shadow().contains("Visual output").click();
cy.get("editor-wc").shadow().find("#root").should("not.contain", "yaw");
Expand Down
1 change: 1 addition & 0 deletions src/assets/stylesheets/ErrorMessage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
color: #7e0305;
background-color: #fde2e1;
padding: $space-0-75 $space-1-25;
overflow-y: auto;

&__content {
padding: 0;
Expand Down
6 changes: 1 addition & 5 deletions src/components/Editor/Output/Output.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@ const Output = () => {
const isEmbedded = useSelector((state) => state.editor.isEmbedded);
const searchParams = new URLSearchParams(window.location.search);
const isBrowserPreview = searchParams.get("browserPreview") === "true";
const usePyodide = searchParams.get("pyodide") === "true";

return (
<>
<ExternalFiles />
<div className="proj-runner-container">
<RunnerFactory
projectType={project.project_type}
usePyodide={usePyodide}
/>
<RunnerFactory projectType={project.project_type} />
{isEmbedded && !isBrowserPreview ? <RunBar embedded /> : null}
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
/* eslint import/no-webpack-loader-syntax: off */
/* eslint-disable react-hooks/exhaustive-deps */

import "../../../../assets/stylesheets/PythonRunner.scss";
import "../../../../../assets/stylesheets/PythonRunner.scss";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import {
setError,
codeRunHandled,
loadingRunner,
} from "../../../../redux/EditorSlice";
} from "../../../../../redux/EditorSlice";
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
import { useMediaQuery } from "react-responsive";
import { MOBILE_MEDIA_QUERY } from "../../../../utils/mediaQueryBreakpoints";
import ErrorMessage from "../../ErrorMessage/ErrorMessage";
import { createError } from "../../../../utils/apiCallHandler";
import { MOBILE_MEDIA_QUERY } from "../../../../../utils/mediaQueryBreakpoints";
import ErrorMessage from "../../../ErrorMessage/ErrorMessage";
import { createError } from "../../../../../utils/apiCallHandler";
import VisualOutputPane from "./VisualOutputPane";
import OutputViewToggle from "../PythonRunner/OutputViewToggle";
import { SettingsContext } from "../../../../utils/settings";
import RunnerControls from "../../../RunButton/RunnerControls";
import OutputViewToggle from "../OutputViewToggle";
import { SettingsContext } from "../../../../../utils/settings";
import RunnerControls from "../../../../RunButton/RunnerControls";

const PyodideRunner = () => {
const PyodideRunner = ({ active }) => {
const pyodideWorker = useMemo(
() => new Worker("./PyodideWorker.js", { type: "module" }),
[],
Expand All @@ -47,6 +47,7 @@ const PyodideRunner = () => {
const showVisualTab = queryParams.get("show_visual_tab") === "true";
const [hasVisual, setHasVisual] = useState(showVisualTab || senseHatAlways);
const [visuals, setVisuals] = useState([]);
const [showRunner, setShowRunner] = useState(active);

useEffect(() => {
pyodideWorker.onmessage = ({ data }) => {
Expand Down Expand Up @@ -79,13 +80,20 @@ const PyodideRunner = () => {
}, []);

useEffect(() => {
if (codeRunTriggered) {
if (codeRunTriggered && active) {
console.log("running with pyodide");
handleRun();
}
}, [codeRunTriggered]);

useEffect(() => {
if (codeRunStopped) {
if (codeRunTriggered) {
setShowRunner(active);
}
}, [codeRunTriggered]);

useEffect(() => {
if (codeRunStopped && active) {
handleStop();
}
}, [codeRunStopped]);
Expand Down Expand Up @@ -116,7 +124,7 @@ const PyodideRunner = () => {
const { content, ctrlD } = await getInputContent(element);

const encoder = new TextEncoder();
const bytes = encoder.encode(content + "\r\n");
const bytes = encoder.encode(content + "\n");

const previousLength = stdinBuffer.current[0];
stdinBuffer.current.set(bytes, previousLength);
Expand Down Expand Up @@ -278,7 +286,12 @@ const PyodideRunner = () => {
};

return (
<div className={`pythonrunner-container`}>
<div
className={`pythonrunner-container pyodiderunner${
active ? " pyodiderunner--active" : ""
}`}
style={{ display: showRunner ? "flex" : "none" }}
>
{isSplitView ? (
<>
{hasVisual && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import configureStore from "redux-mock-store";
import PyodideRunner from "./PyodideRunner";
import { Provider } from "react-redux";
import PyodideWorker, { postMessage } from "./PyodideWorker.mock.js";
import { setError } from "../../../../redux/EditorSlice.js";
import { setError } from "../../../../../redux/EditorSlice.js";

jest.mock("fs");

Expand All @@ -25,19 +25,25 @@ const initialState = {
auth: {},
};

describe("When first loaded", () => {
describe("When active and first loaded", () => {
beforeEach(() => {
const store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);
});

test("it renders successfully", () => {
expect(screen.queryByText("output.textOutput")).toBeInTheDocument();
});

test("it has style display: flex", () => {
expect(document.querySelector(".pyodiderunner")).toHaveStyle(
"display: flex",
);
});
});

describe("When a code run has been triggered", () => {
Expand All @@ -55,7 +61,7 @@ describe("When a code run has been triggered", () => {
});
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);
});
Expand Down Expand Up @@ -94,7 +100,7 @@ describe("When the code has been stopped", () => {
});
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);
});
Expand All @@ -112,7 +118,7 @@ describe("When loading pyodide", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand All @@ -131,7 +137,7 @@ describe("When pyodide has loaded", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand All @@ -151,7 +157,7 @@ describe("When input is required", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand Down Expand Up @@ -192,7 +198,7 @@ describe("When output is received", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand All @@ -215,7 +221,7 @@ describe("When visual output is received", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand Down Expand Up @@ -247,7 +253,7 @@ describe("When an error is received", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand Down Expand Up @@ -279,7 +285,7 @@ describe("When the code run is interrupted", () => {
store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner />,
<PyodideRunner active={true} />,
</Provider>,
);

Expand All @@ -302,3 +308,38 @@ describe("When the code run is interrupted", () => {
);
});
});

describe("When not active and first loaded", () => {
beforeEach(() => {
const store = mockStore(initialState);
render(
<Provider store={store}>
<PyodideRunner active={false} />,
</Provider>,
);
});

test("it renders with display: none", () => {
expect(document.querySelector(".pyodiderunner")).toHaveStyle(
"display: none",
);
});
});

describe("When not active and code run triggered", () => {
beforeEach(() => {
const store = mockStore({
...initialState,
editor: { ...initialState.editor, codeRunTriggered: true },
});
render(
<Provider store={store}>
<PyodideRunner active={false} />,
</Provider>,
);
});

test("it does not send a message to the worker to run the python code", () => {
expect(postMessage).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,9 @@ const vendoredPackages = {
pygal: {
before: () => {
pyodide.registerJsModule("pygal", { ...pygal });
pygal.config.renderChart = (content) =>
pygal.config.renderChart = (content) => {
postMessage({ method: "handleVisual", origin: "pygal", content });
};
},
after: () => {},
},
Expand Down
Loading