diff --git a/.env b/.env index 0d204929..0658ba2d 100644 --- a/.env +++ b/.env @@ -1,11 +1,11 @@ VITE_INITIAL_COMPILER_URL=https://compiler.sensebox.de VITE_BOARD=sensebox-mcu -VITE_BLOCKLY_API=https://api.blockly.sensebox.de +VITE_BLOCKLY_API=https://api.testing.sensebox.de GENERATE_SOURCEMAP=false # in days VITE_SHARE_LINK_EXPIRES=30 -VITE_PROJECTS_ALLOWED_AUTHORS="mario.pesch@uni-muenster.de, p_scha35@uni-muenster.de, e.c-schneider@reedu.de, verena.witte@yahoo.de" +VITE_PROJECTS_ALLOWED_AUTHORS="mario.pesch@uni-muenster.de,p_scha35@uni-muenster.de,e.c-schneider@reedu.de,verena.witte@yahoo.de" diff --git a/cypress/e2e/blockly.cy.js b/cypress/e2e/blockly.cy.js index 3c9a0ac2..236d14c2 100644 --- a/cypress/e2e/blockly.cy.js +++ b/cypress/e2e/blockly.cy.js @@ -89,6 +89,58 @@ describe("Blockly Editor Page Tests", () => { .and("contain.text", "Sprache"); }); + it("[Blockly] changes to tablet mode and compiles code for esp32", () => { + cy.intercept({ + method: "POST", + pathname: "/compile", + }).as("compile"); + + cy.visit("/settings"); + cy.get("#ota-selector").click(); + cy.contains("li", "Activated").click(); + cy.visit("/"); + cy.contains("button", "Close").click(); + cy.get('img[alt="Sensebox ESP"]').click(); + cy.get('button[aria-label="Compile code"]').click(); + + // check if the request was made + cy.wait("@compile", { + responseTimeout: 30000, + requestTimeout: 30000, + }).then((interception) => { + expect(interception.response.statusCode).to.eq(200); + expect(interception.response.body).to.have.property("data"); + expect(interception.response.body.data).to.have.property("id"); + expect(interception.response.body.data.id).to.be.a("string"); + }); + }); + + it("[Blockly] changes to tablet mode and compiles code for MCU", () => { + cy.intercept({ + method: "POST", + pathname: "/compile", + }).as("compile"); + + cy.visit("/settings"); + cy.get("#ota-selector").click(); + cy.contains("li", "Activated").click(); + cy.visit("/"); + cy.contains("button", "Close").click(); + cy.get('img[alt="Sensebox MCU"]').click(); + cy.get('button[aria-label="Compile code"]').click(); + + // check if the request was made + cy.wait("@compile", { + responseTimeout: 30000, + requestTimeout: 30000, + }).then((interception) => { + expect(interception.response.statusCode).to.eq(200); + expect(interception.response.body).to.have.property("data"); + expect(interception.response.body.data).to.have.property("id"); + expect(interception.response.body.data.id).to.be.a("string"); + }); + }); + it("[Blockly] compiles code", () => { // intercept the request to the compiler @@ -98,6 +150,7 @@ describe("Blockly Editor Page Tests", () => { }).as("compile"); cy.visit("/"); + cy.get('img[alt="Sensebox ESP"]').click(); cy.get('button[aria-label="Compile code"]').click(); diff --git a/cypress/e2e/tutorial-flow.cy.js b/cypress/e2e/tutorial-flow.cy.js new file mode 100644 index 00000000..3c01c749 --- /dev/null +++ b/cypress/e2e/tutorial-flow.cy.js @@ -0,0 +1,168 @@ +describe("End-to-End Tutorial Flow", () => { + const timestamp = Date.now(); + const email = `testuser+${timestamp}@example.com`; + const password = "SecurePass123!"; + const initialTitle = "Test123"; + const updatedTitle = "Test123 - Updated"; + const BACKEND_URL = "https://api.testing.sensebox.de"; + let userToken; + let createdTutorialId = ""; + + it("1. registers a new user", () => { + cy.visit("/user/register"); + + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('input[name="confirmPassword"]').type(password); + + cy.get('button[type="submit"]').click(); + + cy.url().should("include", "/user/register/success"); + + // ✅ Prüfe deutschen Erfolgshinweis + cy.contains("Konto erfolgreich erstellt!").should("be.visible"); + }); + + it("2. logs in with the registered user", () => { + cy.visit("/user/login"); + + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); + + // Prüfe, dass wir nicht mehr auf der Login-Seite sind + cy.url().should("not.include", "/login"); + }); + + it("3. navigates to /tutorial/builder", () => { + cy.visit("/user/login"); + + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); + + // Prüfe, dass wir nicht mehr auf der Login-Seite sind + cy.url().should("not.include", "/login"); + + cy.visit("/tutorial/builder"); + + cy.url().should("include", "/tutorial/builder"); + }); + + it("4. creates a new tutorial via UI", () => { + cy.visit("/user/login"); + + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); + + // Prüfe, dass wir nicht mehr auf der Login-Seite sind + cy.url().should("not.include", "/login"); + cy.visit("/tutorial/builder"); + + // Eingaben vornehmen + cy.get("#tutorial-title").type(initialTitle); + cy.get("#tutorial-subtitle").type("Test123"); + cy.get("#accordion_builder_advanced").click(); + + cy.get('button[value="3"]').click(); + + // ❗ Klicke auf den Speichern-Button + cy.contains("button", "Tutorial speichern").click(); + + // Warte auf Erfolgsmeldung + cy.contains("Tutorial erfolgreich gespeichert!").should("be.visible"); + + cy.get("button").contains("Zum Tutorial").click(); + + cy.url().then((url) => { + const match = url.match(/\/tutorial\/([a-f0-9]{24})/); + if (match) { + createdTutorialId = match[1]; + } else { + throw new Error("Tutorial-ID nicht in URL gefunden"); + } + }); + }); + + it("5. views the created tutorial", () => { + cy.visit(`/tutorial/${createdTutorialId}`); + cy.url().should("include", `/tutorial/${createdTutorialId}`); + cy.contains(initialTitle).should("be.visible"); + }); + + it("6. edits the tutorial title and saves", () => { + cy.visit("/user/login"); + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); + + // Prüfe, dass wir nicht mehr auf der Login-Seite sind + cy.url().should("not.include", "/login"); + cy.visit("/tutorial"); + cy.get('img[alt="Sensebox ESP"]').click(); + cy.get(`#edit${createdTutorialId}`).click(); + cy.get("#tutorial-title").type(updatedTitle); + cy.get("#accordion_builder_advanced").click(); + + cy.get('button[value="1"]').click(); + + cy.get(`#edit_hardware`).click(); + cy.get('img[alt="MCU-S2"]').click(); + cy.get('img[alt="Display"]').click(); + cy.get('img[alt="GPS"]').click(); + cy.get("button").contains("Fertig").click(); + cy.contains("button", "Tutorial speichern").click(); + + // Warte auf Erfolgsmeldung + cy.contains("Tutorial erfolgreich gespeichert!").should("be.visible"); + + cy.get("button").contains("Zum Tutorial").click(); + + // Optional: Prüfe auch im UI + cy.contains(updatedTitle).should("be.visible"); + + cy.contains("GPS").should("be.visible"); + cy.contains("Display").should("be.visible"); + cy.contains("MCU-S2").should("be.visible"); + }); + + it("7. deletes the test tutorial", () => { + cy.visit("/user/login"); + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); + + // Prüfe, dass wir nicht mehr auf der Login-Seite sind + cy.url().should("not.include", "/login"); + cy.visit("/tutorial"); + cy.get('img[alt="Sensebox ESP"]').click(); + cy.get(`#delete${createdTutorialId}`).click(); + cy.get("#confirmDelete").click(); + // confirm delete button is gone + cy.get("body").find(`#delete${createdTutorialId}`).should("not.exist"); + }); + + it("8. deletes the test user account", () => { + cy.request("POST", `${BACKEND_URL}/user/login`, { + email, + password, + }).then((loginRes) => { + const token = loginRes.body.token; + + // Nutzer löschen + cy.request({ + method: "DELETE", + url: `${BACKEND_URL}/user/me`, + headers: { + Authorization: `Bearer ${token}`, + }, + }).then((deleteRes) => { + expect(deleteRes.status).to.eq(200); + expect(deleteRes.body.message).to.eq( + "User account successfully deleted.", + ); + }); + }); + }); +}); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index 3eaffffa..bea78b60 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -14,4 +14,19 @@ // *********************************************************** // Import commands.js using ES2015 syntax: -import './commands' \ No newline at end of file +import "./commands"; +// cypress/support/e2e.js +Cypress.on("uncaught:exception", (err, runnable) => { + // Prüfe auf bekannte Monaco-Fehler + if ( + err.message.includes("LoadError") || + err.message.includes("loader.js") || + err.message.includes("monaco-editor") || + err.message.includes("[object Event]") // Dein spezifischer Fehler + ) { + // Verhindere, dass Cypress den Test abbricht + return false; + } + // Bei allen anderen Fehlern: Test abbrechen + return true; +}); diff --git a/index.html b/index.html index a8eb4aaf..143aa917 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,10 @@ +