Skip to content

Commit 550010c

Browse files
cocomarinemaxelkinsgregaCopilot
authored
Improve friendly error message (#1487)
❗ This branch was created by rebasing #1485 to #1483. ### How to enable friendly errors - editor-standalone (for editor): add `friendly_errors_enabled: true` to `options` in src/components/Editor/Project/Project.jsx ### Decisions made upon adding [pfem v. 0.7.0](https://github.com/RaspberryPiFoundation/python-friendly-error-messages/releases) - For now, we are showing `title` and `summary` of friendly errors (potentially `why` bit might also be added later on) - `ErrorMessage` now optionally includes `FriendlyErrorMessage` - The new attribute added and set to `true` in in src/web-component.html (for http://localhost:3011/web-component.html) - Moved `ErrorMessage` to the bottom of input panel above Run button - In mobile screens (`MobileProject`), when the error is triggered after run, the input panel becomes the selected tab so that users can see the error (if no error triggered, the output panel becomes selected) - For Skulpt runner, [the logic of showing error when not in output only mode is now removed](https://github.com/RaspberryPiFoundation/editor-ui/pull/1487/changes#diff-b905586781d8ed679facb77f80b18decb431bb0c1c9576b34e43745c3348a6ab), keeping the same behaviour of no error in output only mode. - For Pyodide runner, [a logic for showing error message when in output only mode](https://github.com/RaspberryPiFoundation/editor-ui/pull/1487/changes#diff-9950a7b6007ef96b6e4ca4e8e0ad4461249437b7121f74511804b3b0139ada2a) is now added - As in `WebcomponentProject.jsx`, EditorInput is not rendered at all in output only mode, resulting in `ErrorMessage` displayed only by Output panel of Pyodide - When there are multiple Python files: - An error from `main.py` shows first. - When there are errors only in files other than `main.py`, they will show (one at a time). - Whatever error is shown will stay even when you select other tabs within the input panel. ### WIP and future pfem - More discussion needed in terms of the possibility of adding `why` of friendly error, copy and UI ### Screen recordings of editor with friendly messages - Multiple python files https://github.com/user-attachments/assets/a70727a4-121a-4c05-859f-f60f2cc29046 - Responsiveness https://github.com/user-attachments/assets/6697cfd4-fca9-4c04-af66-92b181a8e34d - Mobile screen (< 600 px) https://github.com/user-attachments/assets/48736879-ff09-439d-8b1f-fb633a215333 --------- Co-authored-by: Max Elkins <max.elkins@raspberrypi.org> Co-authored-by: Greg Annandale <greg@raspberrypi.org> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 78b3328 commit 550010c

28 files changed

Lines changed: 635 additions & 199 deletions

.yarnrc.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ npmMinimalAgeGate: 7d
1818
npmPreapprovedPackages:
1919
- "@raspberrypifoundation/design-system-react"
2020
- "@raspberrypifoundation/design-system-core"
21-
- "@RaspberryPiFoundation/scratch-gui"
21+
- "@RaspberryPiFoundation/scratch-gui"
22+
- "@raspberrypifoundation/python-friendly-error-messages"

cypress/e2e/spec-wc-pyodide.cy.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
getEditorShadow,
33
getErrorMessage,
4+
getFriendlyErrorMessage,
45
getFileButtonByName,
56
getProgramInput,
67
getPyodideOutput,
@@ -253,3 +254,41 @@ print(text_out)
253254
);
254255
});
255256
});
257+
258+
describe("When friendly errors enabled with pyodide", () => {
259+
beforeEach(() => {
260+
cy.intercept("GET", "**/python-error-copydecks/**", {
261+
fixture: "copydeck.json",
262+
}).as("copydeck");
263+
264+
const params = new URLSearchParams();
265+
params.set("friendly_errors_enabled", "true");
266+
267+
cy.visit({
268+
url: `${origin}?${params.toString()}`,
269+
headers: {
270+
"Cross-Origin-Opener-Policy": "same-origin",
271+
"Cross-Origin-Embedder-Policy": "require-corp",
272+
},
273+
});
274+
cy.window().then((win) => {
275+
Object.defineProperty(win, "crossOriginIsolated", {
276+
value: true,
277+
configurable: true,
278+
});
279+
});
280+
cy.wait("@copydeck");
281+
});
282+
283+
it("shows a friendly error message when an error occurs", () => {
284+
runCode("print(kitten)");
285+
getErrorMessage().should(
286+
"contain",
287+
"NameError: name 'kitten' is not defined on line 1 of main.py",
288+
);
289+
getFriendlyErrorMessage().should(
290+
"contain",
291+
"This variable doesn't exist here",
292+
);
293+
});
294+
});

cypress/e2e/spec-wc-skulpt.cy.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
getErrorMessage,
3+
getFriendlyErrorMessage,
34
getP5Canvas,
45
getPythonConsoleOutput,
56
getSkulptRunner,
@@ -149,3 +150,35 @@ describe("Running the code with skulpt", () => {
149150
);
150151
});
151152
});
153+
154+
describe("When friendly errors enabled with skulpt", () => {
155+
beforeEach(() => {
156+
cy.intercept("GET", "**/python-error-copydecks/**", {
157+
fixture: "copydeck.json",
158+
}).as("copydeck");
159+
160+
const params = new URLSearchParams();
161+
params.set("friendly_errors_enabled", "true");
162+
163+
cy.visit(`${origin}?${params.toString()}`);
164+
cy.window().then((win) => {
165+
Object.defineProperty(win, "crossOriginIsolated", {
166+
value: false,
167+
configurable: true,
168+
});
169+
});
170+
cy.wait("@copydeck");
171+
});
172+
173+
it("shows a friendly error message when an error occurs", () => {
174+
runCode("import turtle\nprint(kitten)");
175+
getErrorMessage().should(
176+
"contain",
177+
"NameError: name 'kitten' is not defined on line 2 of main.py",
178+
);
179+
getFriendlyErrorMessage().should(
180+
"contain",
181+
"This variable doesn't exist here",
182+
);
183+
});
184+
});

cypress/fixtures/copydeck.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"meta": {
3+
"language": "en",
4+
"version": 1
5+
},
6+
7+
"errors": {
8+
"NameError": {
9+
"variants": [
10+
{
11+
"if": {
12+
"not_message": ["is not defined"]
13+
},
14+
"title": "This variable doesn't exist yet",
15+
"summary": "Your code uses the variable {{name}}, but it hasn't been created yet. Check {{loc}}. If you meant to print the text {{name}}, put it in double quotes.",
16+
"why": "Without speech marks Python treats {{name}} as a variable, and this variable does not exist yet.",
17+
"steps": [
18+
"If it is meant to be text, put speech marks around {{name}}.",
19+
"If it is meant to be a variable, make it first (for example: {{name}} = 0).",
20+
"Check spelling and capital letters."
21+
],
22+
"_placeholders": {
23+
"name": "The undefined variable name, e.g. kittens",
24+
"loc": "Where it was used, e.g. line 2 in main.py"
25+
}
26+
},
27+
{
28+
"if": {
29+
"match_message": ["is not defined"]
30+
},
31+
"title": "This variable doesn't exist here",
32+
"summary": "{{name}} might be created somewhere else, but you're using it at {{loc}}. If you meant the text {{name}}, put it in double quotes.",
33+
"why": "A variable created in another place might not be available here.",
34+
"steps": [
35+
"Move the line that makes it to above where you use it.",
36+
"Or set it here just before you use it."
37+
],
38+
"_placeholders": {
39+
"name": "The variable name used out of scope, e.g. total",
40+
"loc": "Where it was referenced, e.g. line 8 in main.py"
41+
}
42+
}
43+
]
44+
}
45+
}
46+
}

cypress/helpers/editor.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ export const getP5Canvas = () => getEditorShadow().find(".p5Canvas");
5757
export const getTurtleOutput = () => getEditorShadow().find("#turtleOutput");
5858

5959
export const getErrorMessage = () =>
60-
getEditorShadow().find(".error-message__content");
60+
getEditorShadow().find(".error-message__python");
61+
62+
export const getFriendlyErrorMessage = () =>
63+
getEditorShadow().find(".friendly-error-message");
6164

6265
export const getTextOutputTab = () =>
6366
getPyodideOutput().findByLabelText("Text output");

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"@hello-pangea/dnd": "^16.2.0",
1616
"@juggle/resize-observer": "^3.3.1",
1717
"@raspberrypifoundation/design-system-react": "^2.7.0",
18-
"@raspberrypifoundation/python-friendly-error-messages": "0.1.6",
18+
"@raspberrypifoundation/python-friendly-error-messages": "0.7.0",
1919
"@react-three/drei": "9.114.3",
2020
"@react-three/fiber": "^8.0.13",
2121
"@reduxjs/toolkit": "^1.6.2",

src/PyodideWorker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ const PyodideWorker = () => {
534534

535535
const line = match ? parseInt(match[2], 10) : "";
536536

537-
return { file, line, mistake, type, info };
537+
return { file, line, mistake, type, info, rawTraceback: error.message };
538538
};
539539

540540
initialisePyodide();

src/assets/error_file.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/icons/cancel_FILL.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/stylesheets/EditorPanel.scss

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
@forward '@raspberrypifoundation/design-system-core/scss/properties/spacing';
2-
@use '@raspberrypifoundation/design-system-core/scss/mixins/typography' as typography;
1+
@forward "@raspberrypifoundation/design-system-core/scss/properties/spacing";
2+
@use "@raspberrypifoundation/design-system-core/scss/mixins/typography" as
3+
typography;
34

45
// Scrollbar-width is needed from scrollbar to show in Chrome
56
.editor-wrapper {
@@ -31,7 +32,6 @@
3132
overscroll-behavior-x: none;
3233
font-family: var(--wc-font-family-monospace);
3334

34-
3535
.cm-content {
3636
flex: 1;
3737
padding-block-start: var(--space-0-5);
@@ -60,5 +60,3 @@
6060
display: none;
6161
}
6262
}
63-
64-

0 commit comments

Comments
 (0)