In einem Versuch, etwas Ähnliches zu tun, haben wir einmal einen Websimulator in Yandex MVP gestartet, in dem der Benutzer Code, Skripte und alles andere auf verschiedene Registerkarten schreiben konnte, und nebenan zeigte er dies alles als Endergebnis an.

MVP zeigte sich gut und wir haben den Web-Simulator auf das Niveau eines vollwertigen Tools gebracht, mit dem wir das Wissen unserer Schüler in Yandex.Practice testen können . Mein Name ist Artem und ich werde Ihnen erzählen, wie wir einen Simulator für das Unterrichten der Webentwicklung erstellt haben, wie es funktioniert und was es kann.
Von außen scheint hier alles einfach zu sein - ich habe den gesamten benutzerdefinierten Code in einen Iframe gestopft, ihn per Postnachricht gepostet, ihn dann auf jede mögliche Weise gerendert und alles funktioniert. Solch eine leicht gepumpte Online-Code-Vorschau.
Aber es gibt Nuancen.
Wie es funktioniert
Zu Beginn haben wir ein mögliches Problem festgestellt: Wenn Sie den Simulator in einer Yandex-Domäne bereitstellen (z. B. im Workshop selbst), besteht eine Wahrscheinlichkeit ungleich Null, dass Benutzer etwas neugieriger werden. Sie nehmen nämlich Code auf und werfen ihn in den Simulator, den der Simulator mit Begeisterung verarbeitet. Der Code ist betrügerisch und entfernt das vorhandene Yandex-Cookie und fügt es in einen Drittanbieter-Service ein. Danach hat der Betrüger Zugriff auf das persönliche Konto des Benutzers in Yandex und alle persönlichen Daten. Dies ist recht einfach zu implementieren, wenn sich dieser Iframe in der Domäne yandex.ru befindet. Aus diesem Grund haben wir auf yandex.net speziell für den Simulator eine separate Domain erstellt und diese als Feynman bezeichnet. Zu Ehren von Richard , ja.
Im Allgemeinen speichert unser Simulator Dateien, die wir an das Backend senden, in den Formaten Nur-Text, JSON und Base64 für Bilder. Dann werden sie in reale Dateien konvertiert und bereits in Form von statischen Dateien verteilt, die wir zum Rendern in den Iframe einfügen können.
Wir spielen hier jedoch nicht nur Syntax-Highlighting, sondern haben auch einen Simulator zum Testen von Wissen. Daher müssen wir diesen Code im laufenden Betrieb testen und überprüfen, dh irgendwie in den Iframe-Prozess einbinden und prüfen, ob der Benutzer alles richtig gemacht hat, beispielsweise wie er die Variable benannt hat oder ob mit den Divs alles in Ordnung ist.
Und hier stoßen wir wieder auf Domains. Der Benutzercode wird, wie ich bereits geschrieben habe, in den Simulator der Feynman-Domain eingefügt und von Yandex aus der Domain praktikum.yandex.ru überprüft . Die Richtlinie des Browsers mit demselben Ursprung ist auf der Hut und erlaubt es Ihnen nicht, die Interna des Iframes zu manipulieren, wenn Sie unterschiedliche Domänen haben.
Aus diesem Grund haben wir uns entschlossen, den Iframe in den Iframe zu stopfen.

Die folgende Situation stellte sich heraus:
- Wir erstellen einen Iframe, der zunächst tatsächlich leer ist.
- Er zeichnet eine Art leere Seite.
- Wir veröffentlichen von unserer Vorderseite aus eine Postnachricht mit einem Link zu dem, was Feynman uns gegeben hat (von wo aus er die Statik hostet).
- Der erste Iframe nimmt diesen Link und ersetzt ihn im src des inneren Iframes.
Infolgedessen kann unser erster Iframe den Code besitzen und mit dem internen Iframe tun, was er will. De facto sind Tests nur eine Bewertungsfunktion, die Zugriff auf Folgendes hat: Dokument, Fenster usw., alles im Iframe. Dies gibt uns die Möglichkeit, einen Test für ein Problem durchzuführen und ihn im Fenster eines bestimmten Iframes auszuführen.
Nicht nur durch Tests
Dann wollten wir einige nützliche Funktionen hinzufügen: ein Terminal, eine Konsole, die Möglichkeit, Benutzerdaten darüber anzuzeigen, was er in das Protokoll geschrieben hat, und andere Freuden. Natürlich haben wir einen vollwertigen adaptiven Modus entwickelt, damit der Benutzer sehen kann, wie das Ergebnis auf Smartphones und Tablets aussehen wird.

Zu diesem Zweck wurde eine spezielle Bibliothek geschrieben, die alle erforderlichen Stile lädt und den Reaktionsmodus emuliert. Wir ändern auch leicht unseren ursprünglichen Iframe und fügen alles hinzu, was es dem Benutzer ermöglicht, sein console.log auf dem Bildschirm anzuzeigen, und nicht nur einige einfache Objekte, sondern auch vollwertige Dokumentdom-Bäume.
Darüber hinaus haben wir gelernt, wie man Vorversuche durchführt. Dies ist nützlich, da es viele Tests gibt, die die gleichen Dinge überprüfen - zum Beispiel, ob der Benutzer es mit Schleifen im Code übertrieben hat, ob er von der Verschachtelung mitgerissen wurde und dergleichen. Es macht wenig Sinn, dies in jedem Test separat zu beschreiben. Deshalb haben wir eine Testbibliothek geschrieben, die eine Reihe spezieller Vortestmethoden enthält, mit denen der Code überprüft wird. Wenn zu diesem Zeitpunkt alles in Ordnung ist, werden der Haupttest und die gelösten Probleme bereits überprüft. Anschließend wird dem Benutzer das Ergebnis angezeigt.
Um das Ergebnis der vorläufigen Tests an den Benutzer zurückzugeben, verwenden wir auch die Postnachricht. Wir senden Nachrichten darüber, unabhängig davon, ob Fehler vorliegen oder ob alles cool ist. Übrigens wird der Code des Schülers im Web-Simulator auch durch den Es-Lint-Linter mit Übersetzung ins Russische überprüft und hebt immer Syntaxfehler hervor.
Probleme mit Codeüberprüfungen (und nicht nur)
Wenn auf der Seite beispielsweise System- oder Browserbenachrichtigungen vorhanden waren, wurde empfohlen, etwas einzugeben. Bei der Ausführung unseres Tests wurden dem Benutzer häufig weiterhin Browserfenster mit Benachrichtigungen und Anforderungen zur Dateneingabe angezeigt. Wir mussten es so machen: Wenn ein Benutzer nur eine Seite mit seinem Code startet, um zu sehen, wie alles funktioniert, muss diese Warnung auch funktionieren. Wenn der Test diesen Code zur Überprüfung ausführt, benötigen wir keine Warnungen mehr, um weiterhin auf dem Bildschirm des Benutzers angezeigt zu werden. Tatsächlich haben wir alle diese Warnungen durch unsere eigenen Stubs für Tests (Mock), Überschreiben von Warnungen, Eingabeaufforderungen und Bestätigen im Fenster ersetzt. Wenn Sie dies nicht tun, erhalten Sie möglicherweise eine Schleife am Ausgang oder eine leere Warnung, die nichts bewirkt.
Übrigens über Endlosschleifen. Das Hauptproblem dabei war, dass der Benutzer wissentlich Code nehmen und schreiben konnte, der gerne in eine Endlosschleife geriet (es gibt nur einen Javascript-Thread im Browser), und infolgedessen legte sich der gesamte Browser hin.
Um dem entgegenzuwirken, haben wir zunächst gelernt, solche Endlosschleifen zu verfolgen, bevor wir Code zur Überprüfung einreichen. Um dies zu tun, war es notwendig, das Benutzerskript irgendwie neu zu schreiben. Wir gingen diesen Weg:
- Für jeden Zyklus fügen wir eine bestimmte Funktion hinzu, die die Anzahl der Anrufe zählt.
- Wenn diese Anzahl von Anrufen 100.000 überschreitet, lösen wir sofort eine Ausnahme aus, die wir auch per Postnachricht zurücksenden. Außerdem überprüfen wir für alle Fälle das Zeitlimit, wenn der Zyklus länger als 10 Sekunden dauert.
- Auf dem Weg verfolgen wir, dass hier etwas nicht stimmt, da eine Ausnahme aufgetreten ist und der Test selbst nicht mehr sinnvoll ist - der Code wird geloopt.
Die Situation mit Links sollte separat vermerkt werden. Angenommen, ein Benutzer in seinem Code verfügt möglicherweise über einige Links, die beim Klicken in einen neuen Tab geöffnet werden sollten, z. B. sein Portfolio oder ein Github-Konto. Und wir brauchten solche Links nicht, um sie direkt im Iframe zu öffnen - andernfalls haben wir anstelle des Iframes eine Seite mit ihrem Link. Es ist notwendig, solche Dinge in einem neuen Tab über Tab zu öffnen. Um einen Link nicht innerhalb des Frames, sondern im übergeordneten Frame zu öffnen, müssen Sie normalerweise nur target = "_ parent" angeben . In unserem Fall mussten wir jedoch einen Handler hinzufügen, der bestimmt, ob der Link extern ist.
Und für alle Links haben wir einen speziellen Handler geschrieben: Wenn wir sehen, dass der Link extern ist, senden wir die Postnachricht nach außen, unterbrechen den Linkhandler selbst (verhindern Sie die Standardeinstellung), und die Postmessage kehrt zu unserer Vorderseite zurück. Wir sehen, dass wir hier einen externen Link haben, und zeigen eine Benachrichtigung an - sind wir sicher, dass wir zu einer externen Site gehen? Und danach öffnen wir neue Tabs.
Und auch Anker, bei ihnen war alles viel einfacher. Sie haben im Iframe einfach nicht funktioniert. Allgemein. Daher haben wir als kleinen Hack Klickereignisse für einen beliebigen Link abonniert. Wenn sich ein Anker darauf befand, haben wir scrollIntoView für ein bestimmtes Element erstellt.
Alle Metadaten (wenn der Benutzer beispielsweise ein auf der HTML-Seite registriertes Favicon oder einen bestimmten Titel hatte) senden wir auch per Postnachricht, nachdem der Iframe geladen wurde. Mit dem querySelector erhalten wir diese beiden Tags, senden sie per Postnachricht an unsere Vorderseite zurück, und die Vorderseite selbst fügt bei Bedarf alle diese Symbole ein. Es scheint eine Kleinigkeit zu sein, aber der Benutzer hat den Eindruck, dass er einen vollwertigen Browser im Browser hat.
Versuche, den Simulator zu umgehen
Unser Websimulator verwendet im Gegensatz zu den Simulatoren, die wir für Python, SQL und andere erstellt haben, das Frontend und nicht das Backend für Überprüfungen. Wenn der Benutzer die Tests korrekt abschließt, wird daher eine entsprechende POST-Anforderung an das Backend gesendet. Im Prinzip kann der Benutzer mit der richtigen Fähigkeit dasselbe tun und eine solche Anfrage manuell senden.
Es gibt ein zweischneidiges Schwert. Einerseits ist es cool, dass eine Person ausreichend an Technologie und grundlegenden Hacks interessiert ist, um dies zu tun. Auf der anderen Seite ist es ein bisschen wie ein Schuss ins Bein, weil unser Simulator nicht dazu dient, formell "OK, du bist großartig, du hast alles getan" zu erhalten, sondern zu lernen, wie man normal arbeitet, deine Fehler bemerkt und sie korrigiert. Im Allgemeinen ist es so, als würde man ins Fitnessstudio gehen, 5 Minuten auf einem Bankdrücken sitzen und dann auf Facebook schreiben: „Ich habe 3 Sätze mit hundert Kilo gemacht“: Man kann das Selbstwertgefühl amüsieren, aber die Erfolge werden dort aufhören.
Aus diesem Grund führen wir diese Prüfung nicht zum Backend, sondern lösen ein ähnliches Problem. Die Leute kommen zum Lernen, um einen echten Job zu bekommen (vielleicht im Workshop selbst), nicht virtuelle Erfolge.
Wir verbessern den Web-Simulator ständig, indem wir sowohl unsere eigene Wunschliste als auch das Feedback der Benutzer verwenden. Daher werden wir Sie weiterhin über seine Entwicklung informieren. Jetzt wird es fertiggestellt, unter Berücksichtigung der Bedürfnisse von Studenten mit einer Anfrage nach bestimmten Technologien, zum Beispiel haben wir die Arbeit mit React und NodeJS hinzugefügt. Der Web-Simulator ist bei weitem der beliebteste von allen, gefolgt vom Python-Simulator - hauptsächlich aufgrund der niedrigeren Eintrittsschwelle und der Beliebtheit der Technologien selbst. Neben dem technischen Teil im Simulator gibt es auch viele Mechaniken für die Arbeit mit interaktiver Theorie (und davon gibt es in all unseren Kursen genug). Es gibt keinen separaten Simulator nur für die QS-Spezialität, bei der wir einen speziellen Satz von Quiz + Ständen erstellt haben, an denen Tester studieren. Übrigens ein paar Tester,Die uns jetzt beim Workshop helfen, sind Absolventen unsererQA-Kurs .
Die Simulatoren für C ++ und der Simulator für maschinelles Lernen sind komplizierter. Wenn Sie interessiert sind, werden wir versuchen, in den nächsten Beiträgen darüber zu sprechen.
Vielen Dank fürs Lesen. Wenn Sie Fragen zu unseren Simulatoren oder zum Practicum als Ganzes haben - schreiben Sie, wir werden antworten.