mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
Add more unit and integration tests (#773)
* Add unit tests for pyloader and pytitle * Add more unit tests for pyrepl, pybox and pyinputbox * Add more tests for pyscript and pyconfig * White space * Fix d3 tests and improve more examples test * Update matplotlib test * Add numpy to dependencies * Address Madhur comments * Update test name
This commit is contained in:
@@ -48,13 +48,13 @@
|
|||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"d3": "https://cdn.skypack.dev/d3@7"
|
"d3": "https://cdn.skypack.dev/pin/d3@v7.6.1-1Q0NZ0WZnbYeSjDusJT3/mode=imports,min/optimized/d3.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import * as d3 from "https://cdn.skypack.dev/d3@7";
|
import * as d3 from "https://cdn.skypack.dev/pin/d3@v7.6.1-1Q0NZ0WZnbYeSjDusJT3/mode=imports,min/optimized/d3.js";
|
||||||
|
|
||||||
const fruits = [
|
const fruits = [
|
||||||
{name: "🍊", count: 21},
|
{name: "🍊", count: 21},
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ dependencies:
|
|||||||
- isort
|
- isort
|
||||||
- codespell
|
- codespell
|
||||||
- pre-commit
|
- pre-commit
|
||||||
|
- pillow
|
||||||
|
- numpy
|
||||||
|
|
||||||
- pip:
|
- pip:
|
||||||
- playwright
|
- playwright
|
||||||
|
|||||||
BIN
pyscriptjs/tests/integration/test_assets/tripcolor.png
Normal file
BIN
pyscriptjs/tests/integration/test_assets/tripcolor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -1,8 +1,13 @@
|
|||||||
|
import base64
|
||||||
|
import io
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import pytest
|
import pytest
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
from .support import ROOT, PyScriptTest
|
from .support import ROOT, PyScriptTest
|
||||||
|
|
||||||
@@ -80,7 +85,6 @@ class TestExamples(PyScriptTest):
|
|||||||
wait_for_render(self.page, "*", '<canvas.*?class=\\"marks\\".*?>')
|
wait_for_render(self.page, "*", '<canvas.*?class=\\"marks\\".*?>')
|
||||||
save_as_png_link = self.page.locator("text=Save as PNG")
|
save_as_png_link = self.page.locator("text=Save as PNG")
|
||||||
see_source_link = self.page.locator("text=View Source")
|
see_source_link = self.page.locator("text=View Source")
|
||||||
|
|
||||||
# These shouldn't be visible since we didn't click the menu
|
# These shouldn't be visible since we didn't click the menu
|
||||||
assert not save_as_png_link.is_visible()
|
assert not save_as_png_link.is_visible()
|
||||||
assert not see_source_link.is_visible()
|
assert not see_source_link.is_visible()
|
||||||
@@ -105,32 +109,66 @@ class TestExamples(PyScriptTest):
|
|||||||
assert self.page.title() == "Bokeh Example"
|
assert self.page.title() == "Bokeh Example"
|
||||||
wait_for_render(self.page, "*", '<div.*?class=\\"bk\\".*?>')
|
wait_for_render(self.page, "*", '<div.*?class=\\"bk\\".*?>')
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="Flaky test #759")
|
|
||||||
def test_d3(self):
|
def test_d3(self):
|
||||||
# XXX improve this test
|
|
||||||
self.goto("examples/d3.html")
|
self.goto("examples/d3.html")
|
||||||
self.wait_for_pyscript()
|
self.wait_for_pyscript()
|
||||||
assert (
|
assert (
|
||||||
self.page.title() == "d3: JavaScript & PyScript visualizations side-by-side"
|
self.page.title() == "d3: JavaScript & PyScript visualizations side-by-side"
|
||||||
)
|
)
|
||||||
wait_for_render(self.page, "*", "<svg.*?>")
|
wait_for_render(self.page, "*", "<svg.*?>")
|
||||||
|
assert "PyScript version" in self.page.content()
|
||||||
|
pyscript_chart = self.page.wait_for_selector("#py")
|
||||||
|
|
||||||
|
# Let's simply assert that the text of the chart is as expected which
|
||||||
|
# means that the chart rendered successfully and with the right text
|
||||||
|
assert "🍊21\n🍇13\n🍏8\n🍌5\n🍐3\n🍋2\n🍎1\n🍉1" in pyscript_chart.inner_text()
|
||||||
|
|
||||||
def test_folium(self):
|
def test_folium(self):
|
||||||
# XXX improve this test
|
|
||||||
self.goto("examples/folium.html")
|
self.goto("examples/folium.html")
|
||||||
self.wait_for_pyscript()
|
self.wait_for_pyscript()
|
||||||
assert self.page.title() == "Folium"
|
assert self.page.title() == "Folium"
|
||||||
wait_for_render(self.page, "*", "<iframe srcdoc=")
|
wait_for_render(self.page, "*", "<iframe srcdoc=")
|
||||||
|
|
||||||
|
# We need to look into the iframe first
|
||||||
|
iframe = self.page.frame_locator("iframe")
|
||||||
|
|
||||||
|
# Just checking that legend was rendered correctly
|
||||||
|
legend = iframe.locator("#legend")
|
||||||
|
assert "Unemployment Rate (%)" in legend.inner_html()
|
||||||
|
|
||||||
|
# Let's check that the zoom buttons are rendered and clickable
|
||||||
|
# Note: if element is not clickable it will timeout
|
||||||
|
zoom_in = iframe.locator("[aria-label='Zoom in']")
|
||||||
|
assert "+" in zoom_in.inner_text()
|
||||||
|
zoom_in.click()
|
||||||
|
zoom_out = iframe.locator("[aria-label='Zoom out']")
|
||||||
|
assert "−" in zoom_out.inner_text()
|
||||||
|
zoom_out.click()
|
||||||
|
|
||||||
def test_matplotlib(self):
|
def test_matplotlib(self):
|
||||||
# XXX improve this test
|
|
||||||
self.goto("examples/matplotlib.html")
|
self.goto("examples/matplotlib.html")
|
||||||
self.wait_for_pyscript()
|
self.wait_for_pyscript()
|
||||||
assert self.page.title() == "Matplotlib"
|
assert self.page.title() == "Matplotlib"
|
||||||
wait_for_render(self.page, "*", "<img src=['\"]data:image")
|
wait_for_render(self.page, "*", "<img src=['\"]data:image")
|
||||||
|
# The image is being rended using base64, lets fetch its source
|
||||||
|
# and replace everything but the actual base64 string.\
|
||||||
|
img_src = (
|
||||||
|
self.page.wait_for_selector("img")
|
||||||
|
.get_attribute("src")
|
||||||
|
.replace("data:image/png;charset=utf-8;base64,", "")
|
||||||
|
)
|
||||||
|
# Finally, let's get the np array from the previous data
|
||||||
|
img_data = np.asarray(Image.open(io.BytesIO(base64.b64decode(img_src))))
|
||||||
|
with Image.open(
|
||||||
|
os.path.join(os.path.dirname(__file__), "test_assets", "tripcolor.png"),
|
||||||
|
) as image:
|
||||||
|
ref_data = np.asarray(image)
|
||||||
|
# Now that we have both images data as a numpy array
|
||||||
|
# let's confirm that they are the same
|
||||||
|
deviation = np.mean(np.abs(img_data - ref_data))
|
||||||
|
assert deviation == 0.0
|
||||||
|
|
||||||
def test_numpy_canvas_fractals(self):
|
def test_numpy_canvas_fractals(self):
|
||||||
# XXX improve this test
|
|
||||||
self.goto("examples/numpy_canvas_fractals.html")
|
self.goto("examples/numpy_canvas_fractals.html")
|
||||||
self.wait_for_pyscript()
|
self.wait_for_pyscript()
|
||||||
assert (
|
assert (
|
||||||
@@ -141,12 +179,57 @@ class TestExamples(PyScriptTest):
|
|||||||
self.page, "*", "<div.*?id=['\"](mandelbrot|julia|newton)['\"].*?>"
|
self.page, "*", "<div.*?id=['\"](mandelbrot|julia|newton)['\"].*?>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Assert that we get the title and canvas for each element
|
||||||
|
mandelbrot = self.page.wait_for_selector("#mandelbrot")
|
||||||
|
assert "Mandelbrot set" in mandelbrot.inner_text()
|
||||||
|
assert "<canvas" in mandelbrot.inner_html()
|
||||||
|
|
||||||
|
julia = self.page.wait_for_selector("#julia")
|
||||||
|
assert "Julia set" in julia.inner_text()
|
||||||
|
assert "<canvas" in julia.inner_html()
|
||||||
|
|
||||||
|
newton = self.page.wait_for_selector("#newton")
|
||||||
|
assert "Newton set" in newton.inner_text()
|
||||||
|
assert "<canvas" in newton.inner_html()
|
||||||
|
|
||||||
|
# Confirm that all fieldsets are rendered correctly
|
||||||
|
poly = newton.wait_for_selector("#poly")
|
||||||
|
assert poly.input_value() == "z**3 - 2*z + 2"
|
||||||
|
|
||||||
|
coef = newton.wait_for_selector("#coef")
|
||||||
|
assert coef.input_value() == "1"
|
||||||
|
|
||||||
|
# Let's now change some x/y values to confirm that they
|
||||||
|
# are editable (is it the best way to test this?)
|
||||||
|
x0 = newton.wait_for_selector("#x0")
|
||||||
|
y0 = newton.wait_for_selector("#y0")
|
||||||
|
|
||||||
|
x0.fill("50")
|
||||||
|
assert x0.input_value() == "50"
|
||||||
|
y0.fill("-25")
|
||||||
|
assert y0.input_value() == "-25"
|
||||||
|
|
||||||
|
# This was the first computation with the default values
|
||||||
|
assert self.console.log.lines[-2] == "Computing Newton set ..."
|
||||||
|
# Confirm that changing the input values, triggered a new computation
|
||||||
|
assert self.console.log.lines[-1] == "Computing Newton set ..."
|
||||||
|
|
||||||
def test_panel(self):
|
def test_panel(self):
|
||||||
# XXX improve this test
|
|
||||||
self.goto("examples/panel.html")
|
self.goto("examples/panel.html")
|
||||||
self.wait_for_pyscript()
|
self.wait_for_pyscript()
|
||||||
assert self.page.title() == "Panel Example"
|
assert self.page.title() == "Panel Example"
|
||||||
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
|
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
|
||||||
|
slider_title = self.page.wait_for_selector(".bk-slider-title")
|
||||||
|
assert slider_title.inner_text() == "Amplitude: 0"
|
||||||
|
|
||||||
|
slider_result = self.page.wait_for_selector(".bk-clearfix")
|
||||||
|
assert slider_result.inner_text() == "Amplitude is: 0"
|
||||||
|
|
||||||
|
amplitude_bar = self.page.wait_for_selector(".noUi-connects")
|
||||||
|
amplitude_bar.click()
|
||||||
|
|
||||||
|
# Let's confirm that slider title changed
|
||||||
|
assert slider_title.inner_text() == "Amplitude: 5"
|
||||||
|
|
||||||
def test_panel_deckgl(self):
|
def test_panel_deckgl(self):
|
||||||
# XXX improve this test
|
# XXX improve this test
|
||||||
@@ -190,7 +273,6 @@ class TestExamples(PyScriptTest):
|
|||||||
)
|
)
|
||||||
assert second_repl_result.text_content() == "4"
|
assert second_repl_result.text_content() == "4"
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="Test seems flaky")
|
|
||||||
def test_repl2(self):
|
def test_repl2(self):
|
||||||
self.goto("examples/repl2.html")
|
self.goto("examples/repl2.html")
|
||||||
self.wait_for_pyscript()
|
self.wait_for_pyscript()
|
||||||
|
|||||||
25
pyscriptjs/tests/unit/pybox.test.ts
Normal file
25
pyscriptjs/tests/unit/pybox.test.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { jest } from "@jest/globals"
|
||||||
|
|
||||||
|
import { PyBox } from "../../src/components/pybox"
|
||||||
|
|
||||||
|
customElements.define('py-box', PyBox)
|
||||||
|
|
||||||
|
describe('PyBox', () => {
|
||||||
|
let instance: PyBox;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
instance = new PyBox();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('PyBox instantiates correctly', async () => {
|
||||||
|
expect(instance).toBeInstanceOf(PyBox)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("test connectedCallback creates pybox div", async () => {
|
||||||
|
expect(instance.innerHTML).toBe("")
|
||||||
|
instance.connectedCallback()
|
||||||
|
|
||||||
|
expect(instance.innerHTML).toBe('<div class=\"py-box\"></div>')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -14,7 +14,7 @@ describe('PyButton', () => {
|
|||||||
expect(instance).toBeInstanceOf(PyButton);
|
expect(instance).toBeInstanceOf(PyButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('confirm that runAfterRuntimeInitialized is called', async () => {
|
it('confirm that runAfterRuntimeInitialized is called', async () => {
|
||||||
const mockedRunAfterRuntimeInitialized = jest
|
const mockedRunAfterRuntimeInitialized = jest
|
||||||
.spyOn(instance, 'runAfterRuntimeInitialized')
|
.spyOn(instance, 'runAfterRuntimeInitialized')
|
||||||
.mockImplementation(jest.fn());
|
.mockImplementation(jest.fn());
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
|
import { jest } from '@jest/globals';
|
||||||
|
import type { AppConfig, RuntimeConfig } from '../../src/runtime';
|
||||||
import { PyConfig } from '../../src/components/pyconfig';
|
import { PyConfig } from '../../src/components/pyconfig';
|
||||||
// inspired by trump typos
|
// inspired by trump typos
|
||||||
const covfefeConfig = {
|
const covfefeConfig = {
|
||||||
"name": "covfefe",
|
name: 'covfefe',
|
||||||
"runtimes": [{
|
runtimes: [
|
||||||
"src": "/demo/covfefe.js",
|
{
|
||||||
"name": "covfefe",
|
src: '/demo/covfefe.js',
|
||||||
"lang": "covfefe"
|
name: 'covfefe',
|
||||||
}],
|
lang: 'covfefe',
|
||||||
"wonerful": "discgrace"
|
},
|
||||||
|
],
|
||||||
|
wonerful: 'discgrace',
|
||||||
};
|
};
|
||||||
|
|
||||||
const covfefeConfigToml = `
|
const covfefeConfigToml = `
|
||||||
@@ -21,20 +25,18 @@ name = "covfefe"
|
|||||||
lang = "covfefe"
|
lang = "covfefe"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
import {jest} from '@jest/globals';
|
|
||||||
|
|
||||||
customElements.define('py-config', PyConfig);
|
customElements.define('py-config', PyConfig);
|
||||||
|
|
||||||
describe('PyConfig', () => {
|
describe('PyConfig', () => {
|
||||||
let instance: PyConfig;
|
let instance: PyConfig;
|
||||||
|
|
||||||
const xhrMockClass = () => ({
|
const xhrMockClass = () => ({
|
||||||
open : jest.fn(),
|
open: jest.fn(),
|
||||||
send : jest.fn(),
|
send: jest.fn(),
|
||||||
responseText : JSON.stringify(covfefeConfig)
|
responseText: JSON.stringify(covfefeConfig),
|
||||||
});
|
});
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)
|
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
instance = new PyConfig();
|
instance = new PyConfig();
|
||||||
@@ -47,57 +49,57 @@ describe('PyConfig', () => {
|
|||||||
it('should load runtime from config and set as script src', () => {
|
it('should load runtime from config and set as script src', () => {
|
||||||
instance.values = covfefeConfig;
|
instance.values = covfefeConfig;
|
||||||
instance.loadRuntimes();
|
instance.loadRuntimes();
|
||||||
expect(document.scripts[0].src).toBe("http://localhost/demo/covfefe.js");
|
expect(document.scripts[0].src).toBe('http://localhost/demo/covfefe.js');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load the default config', ()=> {
|
it('should load the default config', () => {
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
expect(instance.values.name).toBe("pyscript");
|
expect(instance.values.name).toBe('pyscript');
|
||||||
expect(instance.values.author_email).toBe("foo@bar.com");
|
expect(instance.values.author_email).toBe('foo@bar.com');
|
||||||
expect(instance.values.pyscript?.time).not.toBeNull();
|
expect(instance.values.pyscript?.time).not.toBeNull();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(instance.values.runtimes[0].lang).toBe("python");
|
expect(instance.values.runtimes[0].lang).toBe('python');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load the JSON config from inline', ()=> {
|
it('should load the JSON config from inline', () => {
|
||||||
instance.setAttribute("type", "json");
|
instance.setAttribute('type', 'json');
|
||||||
instance.innerHTML = JSON.stringify(covfefeConfig);
|
instance.innerHTML = JSON.stringify(covfefeConfig);
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(instance.values.runtimes[0].lang).toBe("covfefe");
|
expect(instance.values.runtimes[0].lang).toBe('covfefe');
|
||||||
expect(instance.values.pyscript?.time).not.toBeNull();
|
expect(instance.values.pyscript?.time).not.toBeNull();
|
||||||
// version wasn't present in `inline config` but is still set due to merging with default
|
// version wasn't present in `inline config` but is still set due to merging with default
|
||||||
expect(instance.values.version).toBe("0.1");
|
expect(instance.values.version).toBe('0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load the JSON config from src attribute', ()=> {
|
it('should load the JSON config from src attribute', () => {
|
||||||
instance.setAttribute("type", "json");
|
instance.setAttribute('type', 'json');
|
||||||
instance.setAttribute("src", "/covfefe.json");
|
instance.setAttribute('src', '/covfefe.json');
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(instance.values.runtimes[0].lang).toBe("covfefe");
|
expect(instance.values.runtimes[0].lang).toBe('covfefe');
|
||||||
expect(instance.values.pyscript?.time).not.toBeNull();
|
expect(instance.values.pyscript?.time).not.toBeNull();
|
||||||
// wonerful is an extra key supplied by the user and is unaffected by merging process
|
// wonerful is an extra key supplied by the user and is unaffected by merging process
|
||||||
expect(instance.values.wonerful).toBe("discgrace");
|
expect(instance.values.wonerful).toBe('discgrace');
|
||||||
// version wasn't present in `config from src` but is still set due to merging with default
|
// version wasn't present in `config from src` but is still set due to merging with default
|
||||||
expect(instance.values.version).toBe("0.1");
|
expect(instance.values.version).toBe('0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load the JSON config from both inline and src', ()=> {
|
it('should load the JSON config from both inline and src', () => {
|
||||||
instance.setAttribute("type", "json");
|
instance.setAttribute('type', 'json');
|
||||||
instance.innerHTML = JSON.stringify({"version": "0.2a", "wonerful": "highjacked"});
|
instance.innerHTML = JSON.stringify({ version: '0.2a', wonerful: 'highjacked' });
|
||||||
instance.setAttribute("src", "/covfefe.json");
|
instance.setAttribute('src', '/covfefe.json');
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(instance.values.runtimes[0].lang).toBe("covfefe");
|
expect(instance.values.runtimes[0].lang).toBe('covfefe');
|
||||||
expect(instance.values.pyscript?.time).not.toBeNull();
|
expect(instance.values.pyscript?.time).not.toBeNull();
|
||||||
// config from src had an extra key "wonerful" with value "discgrace"
|
// config from src had an extra key "wonerful" with value "discgrace"
|
||||||
// inline config had the same extra key "wonerful" with value "highjacked"
|
// inline config had the same extra key "wonerful" with value "highjacked"
|
||||||
// the merge process works for extra keys that clash as well
|
// the merge process works for extra keys that clash as well
|
||||||
// so the final value is "highjacked" since inline takes precedence over src
|
// so the final value is "highjacked" since inline takes precedence over src
|
||||||
expect(instance.values.wonerful).toBe("highjacked");
|
expect(instance.values.wonerful).toBe('highjacked');
|
||||||
// version wasn't present in `config from src` but is still set due to merging with default and inline
|
// version wasn't present in `config from src` but is still set due to merging with default and inline
|
||||||
expect(instance.values.version).toBe("0.2a");
|
expect(instance.values.version).toBe('0.2a');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to load an inline TOML config', () => {
|
it('should be able to load an inline TOML config', () => {
|
||||||
@@ -105,11 +107,11 @@ describe('PyConfig', () => {
|
|||||||
instance.innerHTML = covfefeConfigToml;
|
instance.innerHTML = covfefeConfigToml;
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(instance.values.runtimes[0].lang).toBe("covfefe");
|
expect(instance.values.runtimes[0].lang).toBe('covfefe');
|
||||||
expect(instance.values.pyscript?.time).not.toBeNull();
|
expect(instance.values.pyscript?.time).not.toBeNull();
|
||||||
// version wasn't present in `inline config` but is still set due to merging with default
|
// version wasn't present in `inline config` but is still set due to merging with default
|
||||||
expect(instance.values.version).toBe("0.1");
|
expect(instance.values.version).toBe('0.1');
|
||||||
expect(instance.values.wonerful).toBe("highjacked");
|
expect(instance.values.wonerful).toBe('highjacked');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('should NOT be able to load an inline config in JSON format with type as TOML', () => {
|
it.failing('should NOT be able to load an inline config in JSON format with type as TOML', () => {
|
||||||
@@ -118,21 +120,56 @@ describe('PyConfig', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it.failing('should NOT be able to load an inline config in TOML format with type as JSON', () => {
|
it.failing('should NOT be able to load an inline config in TOML format with type as JSON', () => {
|
||||||
instance.setAttribute("type", "json");
|
instance.setAttribute('type', 'json');
|
||||||
instance.innerHTML = covfefeConfigToml;
|
instance.innerHTML = covfefeConfigToml;
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('should NOT be able to load an inline TOML config with a JSON config from src with type as toml', () => {
|
it.failing('should NOT be able to load an inline TOML config with a JSON config from src with type as toml', () => {
|
||||||
instance.innerHTML = covfefeConfigToml;
|
instance.innerHTML = covfefeConfigToml;
|
||||||
instance.setAttribute("src", "/covfefe.json");
|
instance.setAttribute('src', '/covfefe.json');
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('should NOT be able to load an inline TOML config with a JSON config from src with type as json', () => {
|
it.failing('should NOT be able to load an inline TOML config with a JSON config from src with type as json', () => {
|
||||||
instance.setAttribute("type", "json");
|
instance.setAttribute('type', 'json');
|
||||||
instance.innerHTML = covfefeConfigToml;
|
instance.innerHTML = covfefeConfigToml;
|
||||||
instance.setAttribute("src", "/covfefe.json");
|
instance.setAttribute('src', '/covfefe.json');
|
||||||
instance.connectedCallback();
|
instance.connectedCallback();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('connectedCallback should call loadRuntimes', async () => {
|
||||||
|
const mockedMethod = jest.fn();
|
||||||
|
instance.loadRuntimes = mockedMethod;
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(mockedMethod).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('confirm connectedCallback happy path', async () => {
|
||||||
|
const mockedMethod = jest.fn();
|
||||||
|
instance.loadRuntimes = mockedMethod;
|
||||||
|
instance.innerHTML = 'test';
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(instance.code).toBe('test');
|
||||||
|
expect(instance.values['0']).toBe('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('log should add new message to the page', async () => {
|
||||||
|
// details are undefined, so let's create a div for it
|
||||||
|
instance.details = document.createElement('div');
|
||||||
|
instance.log('this is a log');
|
||||||
|
|
||||||
|
// @ts-ignore: typescript complains about accessing innerText
|
||||||
|
expect(instance.details.childNodes[0].innerText).toBe('this is a log');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('confirm that calling close would call this.remove', async () => {
|
||||||
|
instance.remove = jest.fn();
|
||||||
|
instance.close();
|
||||||
|
expect(instance.remove).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
66
pyscriptjs/tests/unit/pyinputbox.test.ts
Normal file
66
pyscriptjs/tests/unit/pyinputbox.test.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { jest } from "@jest/globals"
|
||||||
|
|
||||||
|
import { PyInputBox } from "../../src/components/pyinputbox"
|
||||||
|
|
||||||
|
customElements.define('py-inputbox', PyInputBox)
|
||||||
|
|
||||||
|
describe("PyInputBox", () => {
|
||||||
|
let instance: PyInputBox;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
instance = new PyInputBox()
|
||||||
|
instance.runAfterRuntimeInitialized = jest.fn();
|
||||||
|
})
|
||||||
|
|
||||||
|
it("PyInputBox instantiates correctly", async () => {
|
||||||
|
expect(instance).toBeInstanceOf(PyInputBox)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('connectedCallback gets or sets a new id', async () => {
|
||||||
|
expect(instance.id).toBe('');
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
const instanceId = instance.id;
|
||||||
|
// id should be similar to py-4850c8c3-d70d-d9e0-03c1-3cfeb0bcec0d-container
|
||||||
|
expect(instanceId).toMatch(/py-(\w+-){1,5}container/);
|
||||||
|
|
||||||
|
// calling checkId directly should return the same id
|
||||||
|
instance.checkId();
|
||||||
|
expect(instance.id).toEqual(instanceId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('confirm that runAfterRuntimeInitialized is called', async () => {
|
||||||
|
const mockedRunAfterRuntimeInitialized = jest
|
||||||
|
.spyOn(instance, 'runAfterRuntimeInitialized')
|
||||||
|
.mockImplementation(jest.fn());
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(mockedRunAfterRuntimeInitialized).toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('onCallback sets mount_name based on id', async () => {
|
||||||
|
expect(instance.id).toBe('');
|
||||||
|
expect(instance.mount_name).toBe(undefined);
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
const instanceId = instance.id;
|
||||||
|
|
||||||
|
expect(instanceId).toMatch(/py-(\w+-){1,5}container/);
|
||||||
|
expect(instance.mount_name).toBe(instanceId.replace('-container', '').split('-').join('_'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('onCallback updates on_keypress code and function name ', async () => {
|
||||||
|
expect(instance.code).toBe(undefined);
|
||||||
|
expect(instance.innerHTML).toBe('');
|
||||||
|
|
||||||
|
instance.innerHTML = '\ndef on_keypress(e):\n';
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(instance.code).toMatch(/def\son_keypress_py_(\w+)\(e\)/);
|
||||||
|
expect(instance.innerHTML).toContain('<input type="text" class="py-input"');
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
52
pyscriptjs/tests/unit/pyloader.test.ts
Normal file
52
pyscriptjs/tests/unit/pyloader.test.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { jest } from '@jest/globals';
|
||||||
|
import { PyLoader } from "../../src/components/pyloader"
|
||||||
|
import { getLogger } from "../../src/logger"
|
||||||
|
|
||||||
|
customElements.define('py-loader', PyLoader);
|
||||||
|
|
||||||
|
describe('PyLoader', () => {
|
||||||
|
let instance: PyLoader;
|
||||||
|
const logger = getLogger("py-loader")
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
instance = new PyLoader();
|
||||||
|
logger.info = jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('PyLoader instantiates correctly', async () => {
|
||||||
|
expect (instance).toBeInstanceOf(PyLoader);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('connectedCallback adds splash screen', async () => {
|
||||||
|
// innerHTML should be empty
|
||||||
|
expect(instance.innerHTML).toBe("")
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
// This is just checking that we have some ids or class names
|
||||||
|
expect(instance.innerHTML).toContain('pyscript_loading_splash')
|
||||||
|
expect(instance.innerHTML).toContain("spinner")
|
||||||
|
|
||||||
|
expect(instance.mount_name).toBe("")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('confirm calling log will log to console and page', () => {
|
||||||
|
const element = document.createElement('div')
|
||||||
|
element.setAttribute("id", "pyscript-operation-details")
|
||||||
|
|
||||||
|
instance.details = element
|
||||||
|
instance.log("Hello, world!")
|
||||||
|
|
||||||
|
const printedLog = element.getElementsByTagName('p')
|
||||||
|
|
||||||
|
expect(logger.info).toHaveBeenCalledWith("Hello, world!")
|
||||||
|
expect(printedLog[0].innerText).toBe("Hello, world!")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('confirm that calling close removes element', async () => {
|
||||||
|
instance.remove = jest.fn()
|
||||||
|
instance.close()
|
||||||
|
expect(logger.info).toHaveBeenCalledWith("Closing")
|
||||||
|
expect(instance.remove).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'jest';
|
import 'jest';
|
||||||
|
|
||||||
import { PyRepl } from '../../src/components/pyrepl';
|
import { PyRepl } from '../../src/components/pyrepl';
|
||||||
|
|
||||||
customElements.define('py-repl', PyRepl);
|
customElements.define('py-repl', PyRepl);
|
||||||
@@ -13,4 +14,52 @@ describe('PyRepl', () => {
|
|||||||
expect(instance).toBeInstanceOf(PyRepl);
|
expect(instance).toBeInstanceOf(PyRepl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('confirm that codemirror editor is available', async () => {
|
||||||
|
// We are assuming that if editorNode has the 'editor-box' class
|
||||||
|
// then the div was created properly.
|
||||||
|
expect(instance.editorNode.getAttribute('class')).toBe("editor-box")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("connectedCallback gets or sets new id", async () => {
|
||||||
|
expect(instance.id).toBe("")
|
||||||
|
|
||||||
|
instance.connectedCallback()
|
||||||
|
|
||||||
|
const instanceId = instance.id;
|
||||||
|
// id should be similar to py-4850c8c3-d70d-d9e0-03c1-3cfeb0bcec0d
|
||||||
|
expect(instanceId).toMatch(/py-(\w+-){1,4}\w+/);
|
||||||
|
|
||||||
|
// calling checkId directly should return the same id
|
||||||
|
instance.checkId();
|
||||||
|
expect(instance.id).toEqual(instanceId);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('confirm that calling connectedCallback renders the expected elements', async () => {
|
||||||
|
expect(instance.innerHTML).toBe("")
|
||||||
|
instance.innerHTML = "<p>Hello</p>"
|
||||||
|
instance.connectedCallback()
|
||||||
|
|
||||||
|
expect(instance.code).toBe("<p>Hello</p>")
|
||||||
|
expect(instance.editorNode.id).toBe("code-editor")
|
||||||
|
|
||||||
|
// Just check that the button was created
|
||||||
|
expect(instance.btnRun.getAttribute("class")).toBe("absolute repl-play-button")
|
||||||
|
const editorNode = instance.editorNode.innerHTML
|
||||||
|
expect(editorNode).toContain("Python Script Run Button")
|
||||||
|
// Confirm that our innerHTML is set as well
|
||||||
|
expect(editorNode).toContain("Hello")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("confirm that addToOutput updates output element", async () => {
|
||||||
|
expect(instance.outputElement).toBe(undefined)
|
||||||
|
|
||||||
|
// This is just to avoid throwing the test since outputElement is undefined
|
||||||
|
instance.outputElement = document.createElement("div")
|
||||||
|
|
||||||
|
instance.addToOutput("Hello, World!")
|
||||||
|
|
||||||
|
expect(instance.outputElement.innerHTML).toBe("<div>Hello, World!</div>")
|
||||||
|
expect(instance.outputElement.hidden).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
76
pyscriptjs/tests/unit/pyscript.test.ts
Normal file
76
pyscriptjs/tests/unit/pyscript.test.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { jest } from "@jest/globals"
|
||||||
|
|
||||||
|
import { PyScript } from "../../src/components/pyscript"
|
||||||
|
|
||||||
|
customElements.define('py-script', PyScript)
|
||||||
|
|
||||||
|
describe('PyScript', () => {
|
||||||
|
let instance: PyScript;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
instance = new PyScript();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('PyScript instantiates correctly', async () => {
|
||||||
|
expect(instance).toBeInstanceOf(PyScript)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('connectedCallback gets or sets a new id', async () => {
|
||||||
|
expect(instance.id).toBe('');
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
const instanceId = instance.id;
|
||||||
|
// id should be similar to py-4850c8c3-d70d-d9e0-03c1-3cfeb0bcec0d-container
|
||||||
|
expect(instanceId).toMatch(/py-(\w+-){1,5}/);
|
||||||
|
|
||||||
|
// calling checkId directly should return the same id
|
||||||
|
instance.checkId();
|
||||||
|
expect(instance.id).toEqual(instanceId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('connectedCallback creates output div', async () => {
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(instance.innerHTML).toContain('<div class="output">')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('confirm that outputElement has std-out id element', async () => {
|
||||||
|
expect(instance.outputElement).toBe(undefined);
|
||||||
|
|
||||||
|
instance.setAttribute('id', 'std-out')
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(instance.outputElement.getAttribute('id')).toBe("std-out")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('confirm that std-err id element sets errorElement', async () => {
|
||||||
|
expect(instance.outputElement).toBe(undefined);
|
||||||
|
|
||||||
|
instance.setAttribute('id', 'std-err')
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
// We should have an errorElement
|
||||||
|
expect(instance.errorElement.getAttribute('id')).toBe("std-err")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('test output attribute path', async () => {
|
||||||
|
expect(instance.outputElement).toBe(undefined);
|
||||||
|
expect(instance.errorElement).toBe(undefined)
|
||||||
|
|
||||||
|
const createdOutput = document.createElement("output")
|
||||||
|
|
||||||
|
instance.setAttribute('output', 'output')
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(instance.innerHTML).toBe('<div class="output"></div>')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getSourceFromElement returns decoded html', async () => {
|
||||||
|
instance.innerHTML = "<p>Hello</p>"
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
const source = instance.getSourceFromElement();
|
||||||
|
|
||||||
|
expect(source).toBe("<p>Hello</p>")
|
||||||
|
})
|
||||||
|
})
|
||||||
40
pyscriptjs/tests/unit/pytitle.test.ts
Normal file
40
pyscriptjs/tests/unit/pytitle.test.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { jest } from "@jest/globals";
|
||||||
|
|
||||||
|
import { PyTitle } from "../../src/components/pytitle"
|
||||||
|
|
||||||
|
|
||||||
|
customElements.define("py-title", PyTitle);
|
||||||
|
|
||||||
|
|
||||||
|
describe("PyTitle", () => {
|
||||||
|
let instance: PyTitle;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
instance = new PyTitle();
|
||||||
|
})
|
||||||
|
|
||||||
|
it("PyTitle instantiates correctly", async () => {
|
||||||
|
expect(instance).toBeInstanceOf(PyTitle);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("test connectedCallback defaults", async () => {
|
||||||
|
instance.connectedCallback();
|
||||||
|
expect(instance.label).toBe("")
|
||||||
|
expect(instance.mount_name).toBe("")
|
||||||
|
expect(instance.innerHTML).toBe(`<div class=\"py-title\" id=\"\"><h1></h1></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("label renders correctly on the page and updates id", async () => {
|
||||||
|
instance.innerHTML = "Hello, world!"
|
||||||
|
// We need this to test mount_name works properly since connectedCallback
|
||||||
|
// doesn't automatically call checkId (should it?)
|
||||||
|
instance.checkId();
|
||||||
|
|
||||||
|
instance.connectedCallback();
|
||||||
|
|
||||||
|
expect(instance.label).toBe("Hello, world!")
|
||||||
|
// mount_name should be similar to: py_be025f4c_2150_7f2a_1a85_af92970c2a0e
|
||||||
|
expect(instance.mount_name).toMatch(/py_(\w+_){1,5}/);
|
||||||
|
expect(instance.innerHTML).toContain("<h1>Hello, world!</h1>")
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user