为了做类似的事情,我们曾经在Yandex MVP中启动了一个网络模拟器,用户可以在不同的选项卡上编写代码,脚本和所有其他内容,然后他将所有这些显示为最终结果。
MVP表现得很好,我们已经将网络模拟器带到了一个完善的工具水平,可以测试Yandex.Practice中学生的知识。我的名字叫Artem,我将告诉您我们是如何制作一个用于教授Web开发的模拟器,其工作原理和功能的。
从外部看,这里的一切似乎都很简单-我将所有自定义代码填充到iframe中,通过postmessage进行发布,然后以任何可能的方式进行渲染,一切正常。这样稍微抽动的在线代码预览。
但是有细微差别。
这个怎么运作
在开始时,我们注意到了一个可能的问题:如果将模拟器部署在Yandex域(例如,Workshop本身)上,那么用户会感到好奇的可能性不为零。即,他们将把一些代码扔进模拟器,模拟器将热情地处理这些代码。并且该代码将被证明是欺诈性的,并将删除现有的Yandex Cookie,将其插入某些第三方服务中,之后欺诈者将可以访问用户在Yandex中的个人帐户和所有个人数据。如果此iframe位于yandex.ru域中,则很容易实现。因此,我们在yandex.net上专门为模拟器创建了一个单独的域,并将其命名为Feynman。为了纪念理查德,是的。
一般而言,我们的模拟器会将发送给它的文件以纯文本,json和base64格式存储到图像。然后将它们转换为真实文件并以静态形式进行分发,我们可以将其放入iframe中进行渲染。
但是我们不仅在这里突出显示语法,我们还有一个用于测试知识的模拟器。因此,我们需要即时测试并检查此代码,也就是说,以某种方式进入iframe进程,看看用户是否做对了所有事情,例如,他如何命名变量,或者div是否一切正常。
在这里,我们再次遇到领域。正如我已经写过的那样,用户代码已放入Feynman域中的模拟器中,我们从Yandex的praktikum.yandex.ru域中对其进行检查。浏览器的同源策略受到保护,如果您具有不同的域,则不允许您篡改iframe的内部结构。
因此,我们决定将iframe塞入iframe。
出现以下情况:
- 我们创建一个iframe,起初实际上是空的。
- 他画了一些空白页。
- 我们从前台发布了一个帖子消息,其中包含与费曼给我们的内容(他托管静态对象的地方)的链接。
- 第一个iframe使用此链接,并将其替换为内部iframe的src。
结果,我们的第一个iframe可以拥有代码,并可以对内部iframe进行任何所需的操作。实际上,测试只是一个评估功能,可以访问iframe中的所有内容:文档,窗口等。这使我们有机会进行问题测试并在给定的iframe窗口中运行。
不单靠测试
然后,我们想添加一些有用的功能:终端,控制台,显示关于他写到日志的用户数据的功能以及其他乐趣。当然,我们采用了成熟的自适应模式,以便用户可以看到结果在智能手机和平板电脑上的外观。
为此,编写了一个特殊的库,该库加载了所有必要的样式并模拟了响应模式。我们还略微更改了原始iframe,并添加了所有允许用户在屏幕上显示其console.log的内容,不仅包括一些简单的对象,还包括了成熟的文档dom树。
除此之外,我们还学习了如何进行初步测试。这很有用,因为有许多测试可以检查相同的事物-例如,用户是否通过代码循环过度使用它,是否通过嵌套取消了它等。在每个测试中分别描述它并没有多大意义,因此我们编写了一个测试库,该库具有一组用于检查代码的特殊预测试方法。如果在此阶段一切正常,则已经检查了主要测试和已解决的问题,然后将结果显示给用户。
为了将初步测试的结果返回给用户,我们还使用了postmessage-我们通过它发送消息,无论是否有任何错误或一切都很酷。顺便说一句,还通过es-lint linter检查了学生在网络模拟器上的代码,并将其翻译成俄语,并始终突出显示语法错误。
代码审查问题(不仅如此)
例如,如果页面上有任何系统或浏览器通知,建议输入一些内容,那么在运行我们的测试时,用户经常会看到带有通知和数据输入请求的浏览器窗口。我们必须这样做:当用户使用其代码启动页面以查看所有工作原理时,此警报也需要工作。当测试运行此代码进行验证时,我们不再需要警报以继续出现在用户的屏幕上。实际上,我们将所有这些警报替换为我们自己的测试存根(模拟),覆盖警报,提示,在窗口内确认。如果不执行此操作,则可能会在输出中出现循环或没有执行任何操作的空警报。
顺便说一下,关于无限循环。这里的主要问题是,用户可以有意识地获取并编写将快乐地陷入无限循环的代码(浏览器中只有一个javascript线程),结果整个浏览器瘫痪了。
为了解决这个问题,我们首先学会了在提交代码进行审查之前跟踪这样的无限循环。为此,必须以某种方式重写用户脚本,我们采用这种方式:
- 对于每个周期,我们添加一个确定调用次数的函数。
- 如果呼叫数量超过100,000,那么我们会立即引发异常,并通过postmessage发送回去。另外,以防万一,如果周期运行超过10秒,我们将检查超时。
- 一路上,我们跟踪到,由于出现了异常,所以这里出现了问题,并且测试本身不再有意义,因为代码已经循环了。
带链接的情况应单独记录。假设用户的代码中可能包含一些链接,这些链接应在新标签页中单击后打开,例如,他的投资组合或github帐户。而且,我们不需要此类链接即可直接在iframe中打开-否则,我们将拥有一个带有其链接的页面,而不是iframe。有必要通过Tab在新标签中打开这些内容。通常,要打开不在框架内的链接,而要在父框架内打开链接,只需指定target =“ _ parent”。但是在本例中,我们需要添加一个处理程序来确定链接是否在外部。
对于所有链接,我们编写了一个特殊的处理程序:如果看到链接是外部的,则向外发送postmessage,中断链接处理程序本身(防止默认设置),postmessage返回到我们的前面。我们看到我们在这里有一个外部链接,并显示一条通知-我们确定要去一个外部站点吗?之后,我们打开新标签。
还有锚点,有了它们,一切都变得更加简单。他们只是在iframe中不起作用。通常。因此,作为一个小技巧,我们订阅了任何链接上的click事件-如果链接上有锚点,则将scrollIntoView设置为特定元素。
所有元数据(例如,如果用户在HTML页面上注册了收藏夹图标或特定标题),我们也将在iframe加载后通过邮件发送。使用querySelector,我们获得了这两个标签,并通过postmessage将它们发送回我们的前台,并且前台本身在必要时插入了所有这些图标。这似乎是一件小事,但用户会觉得自己在浏览器内部拥有完善的浏览器。
试图绕开模拟器
与我们为Python,SQL和其他工具制作的模拟器不同,我们的网络模拟器使用前端进行检查,而不使用后端。因此,当用户正确完成测试时,相应的POST请求将发送到后端。原则上,具有适当技能的用户可以执行相同操作,并手动发送此类请求。
有一把双刃剑。一方面,很酷的一个人对技术和基本的技巧足够感兴趣,可以做到这一点。另一方面,这有点像腿上的一枪,因为我们的模拟器并不是为了从中正式收到“好的,您总体上很出色,您做了一切”,而是学习如何正常工作,注意并纠正错误。总的来说,这就像去健身房,坐在卧推上5分钟,然后在Facebook上写“三套100公斤重”:您可以自尊,但是成就将止于此。
实际上,这就是为什么我们不将此检查交给后端,它可以解决类似的问题。人们来学习是为了获得一份真正的工作(也许是在讲习班本身中),而不是虚拟的成就。
我们会不断使用自己的愿望清单和用户反馈来改进Web模拟器,因此我们将继续向您介绍其发展情况。现在正在最终确定中,考虑到学生对特定技术的需求,例如,我们增加了与React和NodeJS的合作。到目前为止,Web模拟器是最受欢迎的,其次是Python模拟器-很大程度上是由于入门门槛较低以及技术本身的普及。除了技术部分外,模拟器中还有许多用于交互理论的机制(并且在我们所有的课程中都有足够的机制)。没有仅针对QA专业的单独模拟器,我们在此专门制作了一组测验+测验台,供测试人员学习。顺便说一下,几个测试人员,现在正在帮助我们做研讨会的人是我们的毕业生质量检查课程。
C ++的模拟器和机器学习的模拟器更为复杂,如果您有兴趣,我们将在下一篇文章中尝试讨论它们。
感谢您的阅读,如果您对我们的模拟器或研讨会有任何疑问,请写信给我们,我们会回答。