Skip to content
Draft
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
1 change: 1 addition & 0 deletions src/assets/stylesheets/ErrorMessage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
padding: var(--space-1);
overflow-y: auto;
scrollbar-width: thin;
max-block-size: 30%;

&__error {
padding: 0;
Expand Down
2 changes: 2 additions & 0 deletions src/components/Editor/EditorInput/EditorInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -186,6 +187,7 @@ const EditorInput = () => {
/>
</TabPanel>
))}
<ErrorMessage />
{isMobile ? null : <RunBar />}
</Tabs>
))}
Expand Down
12 changes: 12 additions & 0 deletions src/components/Editor/EditorInput/EditorInput.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -471,7 +472,7 @@ const PyodideRunner = ({
<RunnerControls skinny />
)}
</div>
<ErrorMessage />
{isOutputOnly && <ErrorMessage />}
<TabPanel key={0}>
<pre
className={`pythonrunner-console pythonrunner-console--${settings.fontSize}`}
Expand Down Expand Up @@ -503,7 +504,7 @@ const PyodideRunner = ({
{!isEmbedded && hasVisual && <OutputViewToggle />}
{!isEmbedded && isMobile && <RunnerControls skinny />}
</div>
<ErrorMessage />
{isOutputOnly && <ErrorMessage />}
{hasVisual && (
<TabPanel key={0}>
<VisualOutputPane visuals={visuals} setVisuals={setVisuals} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
openFile,
setFocussedFileIndex,
setFriendlyError,
setIsOutputOnly,
} from "../../../../../redux/EditorSlice.js";
import store from "../../../../../app/store";

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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,
);
Expand Down Expand Up @@ -640,7 +638,6 @@ const SkulptRunner = ({
<RunnerControls skinny />
)}
</div>
<ErrorMessage />
<TabPanel key={0}>
<pre
className={`pythonrunner-console pythonrunner-console--${settings.fontSize}`}
Expand Down Expand Up @@ -677,7 +674,6 @@ const SkulptRunner = ({
{!isEmbedded && showVisualOutput && <OutputViewToggle />}
{!isEmbedded && isMobile && <RunnerControls skinny />}
</div>
{!isOutputOnly && <ErrorMessage />}
<TabPanel key={0}>
<VisualOutputPane ref={visualOutputPaneRef} />
</TabPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Provider store={store}>
<SkulptRunner active={true} />
</Provider>,
);

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(
<Provider store={store}>
<SkulptRunner active={true} />
</Provider>,
);
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(() => {
Expand Down
7 changes: 7 additions & 0 deletions src/components/Mobile/MobileProject/MobileProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -41,6 +42,12 @@ const MobileProject = ({
}
}, [codeRunTriggered, sidebarShowing, withSidebar]);

useEffect(() => {
if (!codeRunTriggered && error && projectType === "python") {
setSelectedTab(withSidebar ? 1 : 0);
}
}, [error, codeRunTriggered, projectType, withSidebar]);

return (
<div
className="proj-container proj-editor-container proj-container--mobile"
Expand Down
43 changes: 43 additions & 0 deletions src/components/Mobile/MobileProject/MobileProject.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,46 @@ describe("When withSidebar is false", () => {
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(
<Provider store={store}>
<MobileProject withSidebar={true} sidebarOptions={["settings"]} />
</Provider>,
);
});

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();
});
});
1 change: 1 addition & 0 deletions src/web-component.html
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
Loading