diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f34bd72b..e0df0c205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Changed - Remove unused `/embedded/projects/:identifier` route (#1013) +- Runner defaults to `pyodide` (#937) ### Fixed @@ -28,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Fix initial value of `user` in `WebComponentLoader` (#1021) - Make `authKey` in e2e web component spec more realistic (#1022) - Remove unused `ComponentStore` (#1023) +- Dynamic switching between `pyodide` and `skulpt` based on user imports (#937) ## [0.23.0] - 2024-05-09 diff --git a/cypress/e2e/missionZero-wc.cy.js b/cypress/e2e/missionZero-wc.cy.js index 3db6aee39..1a59184ae 100644 --- a/cypress/e2e/missionZero-wc.cy.js +++ b/cypress/e2e/missionZero-wc.cy.js @@ -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", @@ -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", @@ -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]") @@ -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'); @@ -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(); @@ -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(); @@ -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(); diff --git a/cypress/e2e/spec-wc.cy.js b/cypress/e2e/spec-wc.cy.js index d2c2abff3..b823c0dbb 100644 --- a/cypress/e2e/spec-wc.cy.js +++ b/cypress/e2e/spec-wc.cy.js @@ -89,6 +89,21 @@ describe("default behaviour", () => { cy.get("editor-wc").shadow().contains("Visual output").click(); cy.get("editor-wc").shadow().find("#root").should("not.contain", "yaw"); }); + + it("does not render astro pi component if sense hat unimported", () => { + cy.get("editor-wc") + .shadow() + .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", "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"); + }); }); describe("when load_remix_disabled is true, e.g. in editor-standalone", () => { diff --git a/public/PyodideServiceWorker.js b/public/PyodideServiceWorker.js new file mode 100644 index 000000000..d6ee62889 --- /dev/null +++ b/public/PyodideServiceWorker.js @@ -0,0 +1,40 @@ +self.addEventListener("install", () => { + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("fetch", (event) => { + if ( + event.request.cache === "only-if-cached" && + event.request.mode !== "same-origin" + ) { + return; + } + + console.log(event.request); + + const interceptedRequests = ["pyodide", "Pyodide"]; + + if (!interceptedRequests.some((str) => event.request.url.includes(str))) { + event.respondWith( + fetch(event.request) + .then((response) => { + console.log(`Intercepted: ${event.request.url}`); + + const body = response.body; + const status = response.status; + const headers = new Headers(response.headers); + const statusText = response.statusText; + + headers.set("Cross-Origin-Embedder-Policy", "require-corp"); + headers.set("Cross-Origin-Opener-Policy", "same-origin"); + + // return new Response(body, { status, statusText, headers }); + }) + .catch(console.error), + ); + } +}); diff --git a/public/PyodideWorker.js b/public/PyodideWorker.js new file mode 100644 index 000000000..da1e2ca1d --- /dev/null +++ b/public/PyodideWorker.js @@ -0,0 +1,321 @@ +/* global importScripts, loadPyodide, SharedArrayBuffer, Atomics, pygal, _internal_sense_hat */ + +// importScripts(`${window.location.origin}/pygal.js`); +// importScripts("http://localhost:8080/pygal.js"); +// importScripts("http://localhost:3011/pygal.js"); +importScripts( + "https://staging-editor.raspberrypi.org/branches/issues_903_Investigate_setting_Pyodide_HTTP_headers_via_a_service_worker/pygal.js", +); +// importScripts(`${window.location.origin}/_internal_sense_hat.js`); +// importScripts("http://localhost:8080/_internal_sense_hat.js"); +// importScripts("http://localhost:3011/_internal_sense_hat.js"); +importScripts( + "https://staging-editor.raspberrypi.org/branches/issues_903_Investigate_setting_Pyodide_HTTP_headers_via_a_service_worker/_internal_sense_hat.js", +); + +const supportsAllFeatures = typeof SharedArrayBuffer !== "undefined"; + +// eslint-disable-next-line no-restricted-globals +if (!supportsAllFeatures && name !== "incremental-features") { + console.warn( + [ + "The code editor will not be able to capture standard input or stop execution because these HTTP headers are not set:", + " - Cross-Origin-Opener-Policy: same-origin", + " - Cross-Origin-Embedder-Policy: require-corp", + "", + "If your app can cope with or without these features, please initialize the web worker with { name: 'incremental-features' } to silence this warning.", + "You can then check for the presence of { stdinBuffer, interruptBuffer } in the handleLoaded message to check whether these features are supported.", + "", + "If you definitely need these features, either configure your server to respond with the HTTP headers above, or register a service worker.", + "Once the HTTP headers are set, the browser will block cross-domain resources so you will need to add 'crossorigin' to