diff --git a/src/assets/stylesheets/ErrorMessage.scss b/src/assets/stylesheets/ErrorMessage.scss index 5f5b5a6b5..232711e00 100644 --- a/src/assets/stylesheets/ErrorMessage.scss +++ b/src/assets/stylesheets/ErrorMessage.scss @@ -8,6 +8,7 @@ padding: var(--space-1); overflow-y: auto; scrollbar-width: thin; + max-block-size: 30%; &__error { padding: 0; diff --git a/src/components/Editor/EditorInput/EditorInput.jsx b/src/components/Editor/EditorInput/EditorInput.jsx index ece8db7ff..98de64507 100644 --- a/src/components/Editor/EditorInput/EditorInput.jsx +++ b/src/components/Editor/EditorInput/EditorInput.jsx @@ -15,6 +15,7 @@ import EditorPanel from "../EditorPanel/EditorPanel"; import DraggableTab from "../DraggableTabs/DraggableTab"; import DroppableTabList from "../DraggableTabs/DroppableTabList"; import RunBar from "../../RunButton/RunBar"; +import ErrorMessage from "../ErrorMessage/ErrorMessage"; import "../../../assets/stylesheets/EditorInput.scss"; import RunnerControls from "../../RunButton/RunnerControls"; @@ -186,6 +187,7 @@ const EditorInput = () => { /> ))} + {isMobile ? null : } ))} diff --git a/src/components/Editor/EditorInput/EditorInput.test.js b/src/components/Editor/EditorInput/EditorInput.test.js index 84b2426aa..30d2b4ab5 100644 --- a/src/components/Editor/EditorInput/EditorInput.test.js +++ b/src/components/Editor/EditorInput/EditorInput.test.js @@ -188,3 +188,15 @@ describe("When read only", () => { expect(screen.queryByText("editorPanel.viewOnly")).toBeInTheDocument(); }); }); + +describe("When there is an error", () => { + beforeEach(() => { + renderEditorInput({ + editor: { ...initialState.editor, error: "An error occurred" }, + }); + }); + + test("The error message is displayed", () => { + expect(screen.getByText("An error occurred")).toBeInTheDocument(); + }); +}); diff --git a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx index 69e7e8cea..aaa7204f4 100644 --- a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx +++ b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx @@ -61,6 +61,7 @@ const PyodideRunner = ({ const userId = user?.profile?.user; const isSplitView = useSelector((s) => s.editor.isSplitView); const isEmbedded = useSelector((s) => s.editor.isEmbedded); + const isOutputOnly = useSelector((s) => s.editor.isOutputOnly); const reactAppApiEndpoint = useSelector((s) => s.editor.reactAppApiEndpoint); const codeRunTriggered = useSelector((s) => s.editor.codeRunTriggered); const codeRunStopped = useSelector((s) => s.editor.codeRunStopped); @@ -471,7 +472,7 @@ const PyodideRunner = ({ )} - + {isOutputOnly && }
}
             {!isEmbedded && isMobile && }
           
-          
+          {isOutputOnly && }
           {hasVisual && (
             
               
diff --git a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.test.js b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.test.js
index 1d5159c78..65cf479b0 100644
--- a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.test.js
+++ b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.test.js
@@ -21,6 +21,7 @@ import {
   openFile,
   setFocussedFileIndex,
   setFriendlyError,
+  setIsOutputOnly,
 } from "../../../../../redux/EditorSlice.js";
 import store from "../../../../../app/store";
 
@@ -439,6 +440,31 @@ describe("When an error is received", () => {
     });
   });
 
+  describe("When output-only is enabled", () => {
+    beforeEach(() => {
+      act(() => {
+        store.dispatch(setIsOutputOnly(true));
+      });
+
+      const worker = PyodideWorker.getLastInstance();
+      worker.postMessageFromWorker({
+        method: "handleError",
+        line: 2,
+        file: "main.py",
+        type: "SyntaxError",
+        info: "something's wrong",
+      });
+    });
+
+    test("it displays the error message", () => {
+      expect(
+        screen.queryByText(
+          "SyntaxError: something's wrong on line 2 of main.py",
+        ),
+      ).toBeInTheDocument();
+    });
+  });
+
   describe("When friendly errors are enabled", () => {
     let loadCopydeckFor;
     let registerAdapter;
diff --git a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx
index 2a0bf630f..9afa1342d 100644
--- a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx
+++ b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx
@@ -23,7 +23,6 @@ import {
   cpythonAdapter,
   friendlyExplain,
 } from "@raspberrypifoundation/python-friendly-error-messages";
-import ErrorMessage from "../../../ErrorMessage/ErrorMessage";
 import ApiCallHandler from "../../../../../utils/apiCallHandler";
 import store from "../../../../../redux/stores/WebComponentStore";
 import VisualOutputPane from "../VisualOutputPane";
@@ -90,7 +89,6 @@ const SkulptRunner = ({
   const user = useSelector((state) => state.auth.user);
   const isSplitView = useSelector((state) => state.editor.isSplitView);
   const isEmbedded = useSelector((state) => state.editor.isEmbedded);
-  const isOutputOnly = useSelector((state) => state.editor.isOutputOnly);
   const codeRunTriggered = useSelector(
     (state) => state.editor.codeRunTriggered,
   );
@@ -640,7 +638,6 @@ const SkulptRunner = ({
                     
                   )}
                 
-                
                 
                   
}
             {!isEmbedded && isMobile && }
           
-          {!isOutputOnly && }
           
             
           
diff --git a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.test.js b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.test.js
index f5f5bf5b1..5e13d5121 100644
--- a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.test.js
+++ b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.test.js
@@ -346,61 +346,6 @@ describe("When an error originates in the sense_hat shim", () => {
   });
 });
 
-describe("When an error has occurred", () => {
-  let mockStore;
-  let store;
-  let initialState;
-
-  beforeEach(() => {
-    const middlewares = [];
-    mockStore = configureStore(middlewares);
-    initialState = {
-      editor: {
-        project: {
-          components: [
-            {
-              name: "main",
-              extension: "py",
-              content: "boom!",
-            },
-          ],
-          image_list: [],
-        },
-        error: "SyntaxError: bad token T_OP on line 1 of main.py",
-      },
-      auth: {
-        user,
-      },
-    };
-  });
-
-  test("Displays error message", () => {
-    store = mockStore(initialState);
-    render(
-      
-        
-      ,
-    );
-
-    expect(
-      screen.getByText("SyntaxError: bad token T_OP on line 1 of main.py"),
-    ).toBeVisible();
-  });
-
-  test("Does not display error message when isOutputOnly state is true", () => {
-    initialState.editor.isOutputOnly = true;
-    store = mockStore(initialState);
-    render(
-      
-        
-      ,
-    );
-    expect(
-      screen.queryByText("SyntaxError: bad token T_OP on line 1 of main.py"),
-    ).not.toBeInTheDocument();
-  });
-});
-
 describe("When there is an import error and the site is cross-origin isolated", () => {
   let store;
   beforeEach(() => {
diff --git a/src/components/Mobile/MobileProject/MobileProject.jsx b/src/components/Mobile/MobileProject/MobileProject.jsx
index 959dbfcd5..72193d9a4 100644
--- a/src/components/Mobile/MobileProject/MobileProject.jsx
+++ b/src/components/Mobile/MobileProject/MobileProject.jsx
@@ -25,6 +25,7 @@ const MobileProject = ({
   const codeRunTriggered = useSelector(
     (state) => state.editor.codeRunTriggered,
   );
+  const error = useSelector((state) => state.editor.error);
   const includesInstructions = sidebarOptions.includes("instructions");
 
   const [selectedTab, setSelectedTab] = useState(1);
@@ -41,6 +42,12 @@ const MobileProject = ({
     }
   }, [codeRunTriggered, sidebarShowing, withSidebar]);
 
+  useEffect(() => {
+    if (!codeRunTriggered && error && projectType === "python") {
+      setSelectedTab(withSidebar ? 1 : 0);
+    }
+  }, [error, codeRunTriggered, projectType, withSidebar]);
+
   return (
     
{ expect(screen.queryByTitle("sidebar.expand")).not.toBeInTheDocument(); }); }); + +describe("When there is an error after code has been run", () => { + beforeEach(() => { + const initialState = { + editor: { + project: { + project_type: "python", + components: [ + { + name: "main", + extension: "py", + content: "print('hello')", + }, + ], + image_list: [], + user_id: user.profile.user, + }, + codeRunTriggered: false, + openFiles: [["main.py"]], + focussedFileIndices: [0], + error: "An error occurred", + }, + auth: { + user: user, + }, + }; + const store = mockStore(initialState); + render( + + + , + ); + }); + + test("The input tab is selected", () => { + const inputTab = screen.getByText("mobile.code").parentElement; + expect(inputTab).toHaveClass("react-tabs__tab--selected"); + }); + + test("The error message is displayed", () => { + expect(screen.getByText("An error occurred")).toBeInTheDocument(); + }); +}); diff --git a/src/web-component.html b/src/web-component.html index 0b637350d..cdc780a88 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -96,6 +96,7 @@ newWebComp.setAttribute("with_projectbar", "true"); newWebComp.setAttribute("with_sidebar", "true"); + newWebComp.setAttribute("friendly_errors_enabled", "true"); newWebComp.setAttribute( "sidebar_options", JSON.stringify([