mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
Merge pull request #1675 from RustPython/coolreader18/demo-improvements
Demo improvements
This commit is contained in:
10 files changed
+364
-230
No files matched your search
@@ -9,14 +9,13 @@
|
||||
"xterm": "^3.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wasm-tool/wasm-pack-plugin": "0.2.0",
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"css-loader": "^2.0.1",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.1.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.4.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"raw-loader": "^1.0.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"serve": "^11.0.2",
|
||||
"start-server-and-test": "^1.7.11",
|
||||
"webpack": "^4.16.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5"
|
||||
@@ -25,8 +24,7 @@
|
||||
"dev": "webpack-dev-server -d",
|
||||
"build": "webpack",
|
||||
"dist": "webpack --mode production",
|
||||
"test": "cd ../tests; pipenv run pytest",
|
||||
"ci": "start-server-and-test 'npm run build && serve dist/ -n -p 8080' :8080 test"
|
||||
"test": "webpack && cd ../tests && pipenv run pytest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import browser
|
||||
import functools
|
||||
|
||||
|
||||
# just setting up the framework, skip to the bottom to see the real code
|
||||
|
||||
ready = object()
|
||||
go = object()
|
||||
|
||||
|
||||
def run(coro, *, payload=None, error=False):
|
||||
send = coro.throw if error else coro.send
|
||||
try:
|
||||
cmd = send(payload)
|
||||
except StopIteration:
|
||||
return
|
||||
if cmd is ready:
|
||||
coro.send(
|
||||
(
|
||||
lambda *args: run(coro, payload=args),
|
||||
lambda *args: run(coro, payload=args, error=True),
|
||||
)
|
||||
)
|
||||
elif cmd is go:
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError(f"expected cmd to be ready or go, got {cmd}")
|
||||
|
||||
|
||||
class JSFuture:
|
||||
def __init__(self, prom):
|
||||
self._prom = prom
|
||||
|
||||
def __await__(self):
|
||||
done, error = yield ready
|
||||
self._prom.then(done, error)
|
||||
res, = yield go
|
||||
return res
|
||||
|
||||
|
||||
def wrap_prom_func(func):
|
||||
@functools.wraps(func)
|
||||
async def wrapper(*args, **kwargs):
|
||||
return await JSFuture(func(*args, **kwargs))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
fetch = wrap_prom_func(browser.fetch)
|
||||
|
||||
###################
|
||||
# Real code start #
|
||||
###################
|
||||
|
||||
|
||||
async def main(delay):
|
||||
url = f"https://httpbin.org/delay/{delay}"
|
||||
print(f"fetching {url}...")
|
||||
res = await fetch(
|
||||
url, response_format="json", headers={"X-Header-Thing": "rustpython is neat!"}
|
||||
)
|
||||
print(f"got res from {res['url']}:")
|
||||
print(res, end="\n\n")
|
||||
|
||||
|
||||
for delay in range(3):
|
||||
run(main(delay))
|
||||
print()
|
||||
@@ -9,9 +9,9 @@
|
||||
<p>
|
||||
RustPython is a Python interpreter written in Rust. This demo is
|
||||
compiled from Rust to WebAssembly so it runs in the browser.<br>
|
||||
Please input your Python code below and click <kbd>Run</kbd>
|
||||
Input your Python code below and click <kbd>Run</kbd>
|
||||
(or <kbd>Ctrl+Enter</kbd>), or you can open up your
|
||||
browser's devtools and play with <code>rp.pyEval('print("a")')</code>
|
||||
browser's devtools and play with <code>rp.pyEval('1 + 1')</code>
|
||||
</p>
|
||||
<div id="code-wrapper">
|
||||
<textarea id="code">
|
||||
@@ -31,9 +31,13 @@
|
||||
<textarea id="console" readonly>Loading...</textarea>
|
||||
|
||||
<h3>Interactive shell</h3>
|
||||
<div id="terminal"></div>
|
||||
<div id="terminal"></div>
|
||||
|
||||
<p>Here's some info regarding the <code>rp.pyEval()</code> function</p>
|
||||
<p>
|
||||
Here's some info regarding the <code>rp.pyEval()</code>,
|
||||
<code>rp.pyExec()</code>, and <code>rp.pyExecSingle()</code>
|
||||
functions
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
You can return variables from python and get them returned to
|
||||
|
||||
+154
-7
@@ -1,11 +1,158 @@
|
||||
import './style.css';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'xterm/lib/xterm.css';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/python/python';
|
||||
import 'codemirror/addon/comment/comment';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import { Terminal } from 'xterm';
|
||||
import LocalEchoController from 'local-echo';
|
||||
|
||||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `index.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import('./main.js').catch(e => {
|
||||
console.error('Error importing `main.js`:', e);
|
||||
document.getElementById('error').textContent = e;
|
||||
let rp;
|
||||
|
||||
// A dependency graph that contains any wasm must be imported asynchronously.
|
||||
import('rustpython')
|
||||
.then(rustpy => {
|
||||
rp = rustpy;
|
||||
// so people can play around with it
|
||||
window.rp = rustpy;
|
||||
onReady();
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('Error importing `rustpython`:', e);
|
||||
document.getElementById('error').textContent = e;
|
||||
});
|
||||
|
||||
const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
|
||||
extraKeys: {
|
||||
'Ctrl-Enter': runCodeFromTextarea,
|
||||
'Cmd-Enter': runCodeFromTextarea,
|
||||
'Shift-Tab': 'indentLess',
|
||||
'Ctrl-/': 'toggleComment',
|
||||
'Cmd-/': 'toggleComment',
|
||||
Tab: editor => {
|
||||
var spaces = Array(editor.getOption('indentUnit') + 1).join(' ');
|
||||
editor.replaceSelection(spaces);
|
||||
}
|
||||
},
|
||||
lineNumbers: true,
|
||||
mode: 'text/x-python',
|
||||
indentUnit: 4,
|
||||
autofocus: true
|
||||
});
|
||||
|
||||
const consoleElement = document.getElementById('console');
|
||||
const errorElement = document.getElementById('error');
|
||||
|
||||
function runCodeFromTextarea() {
|
||||
// Clean the console and errors
|
||||
consoleElement.value = '';
|
||||
errorElement.textContent = '';
|
||||
|
||||
const code = editor.getValue();
|
||||
try {
|
||||
rp.pyExec(code, {
|
||||
stdout: output => {
|
||||
const shouldScroll =
|
||||
consoleElement.scrollHeight - consoleElement.scrollTop ===
|
||||
consoleElement.clientHeight;
|
||||
consoleElement.value += output;
|
||||
if (shouldScroll) {
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof WebAssembly.RuntimeError) {
|
||||
err = window.__RUSTPYTHON_ERROR || err;
|
||||
}
|
||||
errorElement.textContent = err;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
const snippets = document.getElementById('snippets');
|
||||
|
||||
function updateSnippet() {
|
||||
const selected = snippets.value;
|
||||
|
||||
// the require here creates a webpack context; it's fine to use it
|
||||
// dynamically.
|
||||
// https://webpack.js.org/guides/dependency-management/
|
||||
const {
|
||||
default: snippet
|
||||
} = require(`raw-loader!../snippets/${selected}.py`);
|
||||
|
||||
editor.setValue(snippet);
|
||||
runCodeFromTextarea();
|
||||
}
|
||||
|
||||
const term = new Terminal();
|
||||
term.open(document.getElementById('terminal'));
|
||||
|
||||
const localEcho = new LocalEchoController(term);
|
||||
|
||||
let terminalVM;
|
||||
|
||||
function getPrompt(name) {
|
||||
terminalVM.exec(`
|
||||
try:
|
||||
import sys as __sys
|
||||
__prompt = __sys.${name}
|
||||
except:
|
||||
__prompt = ''
|
||||
finally:
|
||||
del __sys
|
||||
`);
|
||||
return String(terminalVM.eval('__prompt'));
|
||||
}
|
||||
|
||||
async function readPrompts() {
|
||||
let continuing = false;
|
||||
|
||||
while (true) {
|
||||
const ps1 = getPrompt('ps1');
|
||||
const ps2 = getPrompt('ps2');
|
||||
let input;
|
||||
if (continuing) {
|
||||
const prom = localEcho.read(ps2, ps2);
|
||||
localEcho._activePrompt.prompt = ps1;
|
||||
localEcho._input = localEcho.history.entries.pop() + '\n';
|
||||
localEcho._cursor = localEcho._input.length;
|
||||
localEcho._active = true;
|
||||
input = await prom;
|
||||
if (!input.endsWith('\n')) continue;
|
||||
} else {
|
||||
input = await localEcho.read(ps1, ps2);
|
||||
}
|
||||
try {
|
||||
terminalVM.execSingle(input);
|
||||
} catch (err) {
|
||||
if (err instanceof SyntaxError && err.message.includes('EOF')) {
|
||||
continuing = true;
|
||||
continue;
|
||||
} else if (err instanceof WebAssembly.RuntimeError) {
|
||||
err = window.__RUSTPYTHON_ERROR || err;
|
||||
}
|
||||
localEcho.println(err);
|
||||
}
|
||||
continuing = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
snippets.addEventListener('change', updateSnippet);
|
||||
document
|
||||
.getElementById('run-btn')
|
||||
.addEventListener('click', runCodeFromTextarea);
|
||||
// Run once for demo
|
||||
runCodeFromTextarea();
|
||||
|
||||
terminalVM = rp.vmStore.init('term_vm');
|
||||
terminalVM.setStdout(data => localEcho.println(data));
|
||||
readPrompts().catch(err => console.error(err));
|
||||
|
||||
// so that the test knows that we're ready
|
||||
const readyElement = document.createElement('div');
|
||||
readyElement.id = 'rp_loaded';
|
||||
document.head.appendChild(readyElement);
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import * as rp from 'rustpython';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/python/python';
|
||||
import 'codemirror/addon/comment/comment';
|
||||
import { Terminal } from 'xterm';
|
||||
import LocalEchoController from 'local-echo';
|
||||
|
||||
// so people can play around with it
|
||||
window.rp = rp;
|
||||
|
||||
const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
|
||||
extraKeys: {
|
||||
'Ctrl-Enter': runCodeFromTextarea,
|
||||
'Cmd-Enter': runCodeFromTextarea,
|
||||
'Shift-Tab': 'indentLess',
|
||||
'Ctrl-/': 'toggleComment',
|
||||
'Cmd-/': 'toggleComment',
|
||||
Tab: editor => {
|
||||
var spaces = Array(editor.getOption('indentUnit') + 1).join(' ');
|
||||
editor.replaceSelection(spaces);
|
||||
}
|
||||
},
|
||||
lineNumbers: true,
|
||||
mode: 'text/x-python',
|
||||
indentUnit: 4,
|
||||
autofocus: true
|
||||
});
|
||||
|
||||
const consoleElement = document.getElementById('console');
|
||||
const errorElement = document.getElementById('error');
|
||||
|
||||
function runCodeFromTextarea() {
|
||||
// Clean the console and errors
|
||||
consoleElement.value = '';
|
||||
errorElement.textContent = '';
|
||||
|
||||
const code = editor.getValue();
|
||||
try {
|
||||
rp.pyExec(code, {
|
||||
stdout: output => {
|
||||
const shouldScroll =
|
||||
consoleElement.scrollHeight - consoleElement.scrollTop ===
|
||||
consoleElement.clientHeight;
|
||||
consoleElement.value += output;
|
||||
if (shouldScroll) {
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof WebAssembly.RuntimeError) {
|
||||
err = window.__RUSTPYTHON_ERROR || err;
|
||||
}
|
||||
errorElement.textContent = err;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById('run-btn')
|
||||
.addEventListener('click', runCodeFromTextarea);
|
||||
|
||||
const snippets = document.getElementById('snippets');
|
||||
|
||||
const updateSnippet = () => {
|
||||
const selected = snippets.value;
|
||||
|
||||
// the require here creates a webpack context; it's fine to use it
|
||||
// dynamically.
|
||||
// https://webpack.js.org/guides/dependency-management/
|
||||
const snippet = require(`raw-loader!../snippets/${selected}.py`);
|
||||
|
||||
editor.setValue(snippet);
|
||||
runCodeFromTextarea();
|
||||
};
|
||||
|
||||
snippets.addEventListener('change', updateSnippet);
|
||||
|
||||
// Run once for demo (updateSnippet b/c the browser might try to keep the same
|
||||
// option selected for the `select`, but the textarea won't be updated)
|
||||
updateSnippet();
|
||||
|
||||
const term = new Terminal();
|
||||
term.open(document.getElementById('terminal'));
|
||||
|
||||
const localEcho = new LocalEchoController(term);
|
||||
|
||||
const terminalVM = rp.vmStore.init('term_vm');
|
||||
|
||||
terminalVM.setStdout(data => localEcho.println(data));
|
||||
|
||||
function getPrompt(name) {
|
||||
terminalVM.exec(`
|
||||
try:
|
||||
import sys as __sys
|
||||
__prompt = __sys.${name}
|
||||
except:
|
||||
__prompt = ''
|
||||
finally:
|
||||
del __sys
|
||||
`);
|
||||
return String(terminalVM.eval('__prompt'));
|
||||
}
|
||||
|
||||
async function readPrompts() {
|
||||
let continuing = false;
|
||||
|
||||
while (true) {
|
||||
const ps1 = getPrompt('ps1');
|
||||
const ps2 = getPrompt('ps2');
|
||||
let input;
|
||||
if (continuing) {
|
||||
const prom = localEcho.read(ps2, ps2);
|
||||
localEcho._activePrompt.prompt = ps1;
|
||||
localEcho._input = localEcho.history.entries.pop() + '\n';
|
||||
localEcho._cursor = localEcho._input.length;
|
||||
localEcho._active = true;
|
||||
input = await prom;
|
||||
if (!input.endsWith('\n')) continue;
|
||||
} else {
|
||||
input = await localEcho.read(ps1, ps2);
|
||||
}
|
||||
try {
|
||||
terminalVM.execSingle(input);
|
||||
} catch (err) {
|
||||
if (err instanceof SyntaxError && err.message.includes('EOF')) {
|
||||
continuing = true;
|
||||
continue;
|
||||
} else if (err instanceof WebAssembly.RuntimeError) {
|
||||
err = window.__RUSTPYTHON_ERROR || err;
|
||||
}
|
||||
localEcho.println(err);
|
||||
}
|
||||
continuing = false;
|
||||
}
|
||||
}
|
||||
|
||||
readPrompts().catch(err => console.error(err));
|
||||
@@ -1,6 +1,8 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -31,6 +33,7 @@ module.exports = (env = {}) => {
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'src/index.ejs',
|
||||
|
||||
@@ -20,4 +20,4 @@ export PATH=$PATH:$PWD/geckodriver
|
||||
pip install pipenv
|
||||
(cd wasm/tests; pipenv install)
|
||||
|
||||
(cd wasm/demo; npm install; npm run build; npm run ci)
|
||||
(cd wasm/demo; npm install; npm run test)
|
||||
@@ -0,0 +1,108 @@
|
||||
import subprocess
|
||||
import os
|
||||
import time
|
||||
import socket
|
||||
import atexit
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
PORT = 8080
|
||||
|
||||
HTTP_SCRIPT = f"""
|
||||
import mimetypes
|
||||
mimetypes.add_type("application/wasm", ".wasm")
|
||||
import http.server
|
||||
http.server.test(HandlerClass=http.server.SimpleHTTPRequestHandler, port={PORT})
|
||||
"""
|
||||
|
||||
demo_dist = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../demo/dist/")
|
||||
|
||||
server_proc = None
|
||||
|
||||
|
||||
def pytest_sessionstart(session):
|
||||
global server_proc
|
||||
server_proc = subprocess.Popen(
|
||||
["python3", "-c", HTTP_SCRIPT],
|
||||
cwd=demo_dist,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
wait_for_port(PORT)
|
||||
|
||||
|
||||
def pytest_sessionfinish(session):
|
||||
global server_proc
|
||||
server_proc.terminate()
|
||||
server_proc = None
|
||||
|
||||
|
||||
atexit.register(lambda: server_proc and server_proc.terminate())
|
||||
|
||||
|
||||
# From https://gist.github.com/butla/2d9a4c0f35ea47b7452156c96a4e7b12
|
||||
def wait_for_port(port, host="0.0.0.0", timeout=5.0):
|
||||
"""Wait until a port starts accepting TCP connections.
|
||||
Args:
|
||||
port (int): Port number.
|
||||
host (str): Host address on which the port should exist.
|
||||
timeout (float): In seconds. How long to wait before raising errors.
|
||||
Raises:
|
||||
TimeoutError: The port isn't accepting connection after time specified in `timeout`.
|
||||
"""
|
||||
start_time = time.perf_counter()
|
||||
while True:
|
||||
try:
|
||||
with socket.create_connection((host, port), timeout=timeout):
|
||||
break
|
||||
except OSError as ex:
|
||||
time.sleep(0.01)
|
||||
if time.perf_counter() - start_time >= timeout:
|
||||
raise TimeoutError(
|
||||
"Waited too long for the port {} on host {} to start accepting "
|
||||
"connections.".format(port, host)
|
||||
) from ex
|
||||
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.common.exceptions import JavascriptException
|
||||
|
||||
|
||||
class Driver(webdriver.Firefox):
|
||||
def _print_panic(self):
|
||||
stack = self.execute_script(
|
||||
"return (window.__RUSTPYTHON_ERROR_MSG || '') + '\\n' + (window.__RUSTPYTHON_ERROR_STACK || '')"
|
||||
)
|
||||
if stack.strip():
|
||||
print(f"RustPython panic stack:", stack, file=sys.stderr, sep="\n")
|
||||
|
||||
def execute_script(self, *args, **kwargs):
|
||||
try:
|
||||
return super().execute_script(*args, **kwargs)
|
||||
except JavascriptException:
|
||||
self._print_panic()
|
||||
raise
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wdriver(request):
|
||||
options = Options()
|
||||
options.add_argument("-headless")
|
||||
driver = Driver(options=options)
|
||||
try:
|
||||
driver.get(f"http://0.0.0.0:{PORT}")
|
||||
WebDriverWait(driver, 5).until(
|
||||
EC.presence_of_element_located((By.ID, "rp_loaded"))
|
||||
)
|
||||
except JavascriptException:
|
||||
driver._print_panic()
|
||||
driver.quit()
|
||||
raise
|
||||
|
||||
yield driver
|
||||
|
||||
driver.quit()
|
||||
+6
-34
@@ -17,39 +17,11 @@ vm.destroy();
|
||||
return output;
|
||||
"""
|
||||
|
||||
def print_stack(driver):
|
||||
stack = driver.execute_script(
|
||||
"return window.__RUSTPYTHON_ERROR_MSG + '\\n' + window.__RUSTPYTHON_ERROR_STACK"
|
||||
)
|
||||
print(f"RustPython error stack:\n{stack}", file=sys.stderr)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def driver(request):
|
||||
options = Options()
|
||||
options.add_argument('-headless')
|
||||
driver = webdriver.Firefox(options=options)
|
||||
try:
|
||||
driver.get("http://localhost:8080")
|
||||
except Exception as e:
|
||||
print_stack(driver)
|
||||
raise
|
||||
time.sleep(5)
|
||||
yield driver
|
||||
driver.close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("script, output",
|
||||
[
|
||||
("print(5)", "5"),
|
||||
("a=5;b=4;print(a+b)", "9")
|
||||
]
|
||||
@pytest.mark.parametrize(
|
||||
"script, output", [("print(5)", "5"), ("a=5;b=4;print(a+b)", "9")]
|
||||
)
|
||||
def test_demo(driver, script, output):
|
||||
script = RUN_CODE_TEMPLATE.format(script)
|
||||
try:
|
||||
script_output = driver.execute_script(script)
|
||||
except Exception as e:
|
||||
print_stack(driver)
|
||||
raise
|
||||
assert script_output.strip() == output
|
||||
def test_demo(wdriver, script, output):
|
||||
script = RUN_CODE_TEMPLATE.format(script)
|
||||
script_output = wdriver.execute_script(script)
|
||||
assert script_output.strip() == output
|
||||
@@ -1,46 +1,18 @@
|
||||
import time
|
||||
import sys
|
||||
def test_eval_mode(wdriver):
|
||||
assert wdriver.execute_script("return window.rp.pyEval('1+1')") == 2
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
import pytest
|
||||
def test_exec_mode(wdriver):
|
||||
assert wdriver.execute_script("return window.rp.pyExec('1+1')") is None
|
||||
|
||||
def print_stack(driver):
|
||||
stack = driver.execute_script(
|
||||
"return window.__RUSTPYTHON_ERROR_MSG + '\\n' + window.__RUSTPYTHON_ERROR_STACK"
|
||||
)
|
||||
print(f"RustPython error stack:\n{stack}", file=sys.stderr)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def driver(request):
|
||||
options = Options()
|
||||
options.add_argument('-headless')
|
||||
driver = webdriver.Firefox(options=options)
|
||||
try:
|
||||
driver.get("http://localhost:8080")
|
||||
except Exception as e:
|
||||
print_stack(driver)
|
||||
raise
|
||||
time.sleep(5)
|
||||
yield driver
|
||||
driver.close()
|
||||
|
||||
|
||||
def test_eval_mode(driver):
|
||||
assert driver.execute_script("return window.rp.pyEval('1+1')") == 2
|
||||
|
||||
def test_exec_mode(driver):
|
||||
assert driver.execute_script("return window.rp.pyExec('1+1')") is None
|
||||
|
||||
def test_exec_single_mode(driver):
|
||||
assert driver.execute_script("return window.rp.pyExecSingle('1+1')") == 2
|
||||
assert driver.execute_script(
|
||||
def test_exec_single_mode(wdriver):
|
||||
assert wdriver.execute_script("return window.rp.pyExecSingle('1+1')") == 2
|
||||
assert wdriver.execute_script(
|
||||
"""
|
||||
var output = [];
|
||||
save_output = function(text) {{
|
||||
output.push(text)
|
||||
output.push(text)
|
||||
}};
|
||||
window.rp.pyExecSingle('1+1\\n2+2',{stdout: save_output});
|
||||
return output;
|
||||
""") == ['2\n', '4\n']
|
||||
"""
|
||||
) == ["2\n", "4\n"]
|
||||
Reference in new issue
Block a user