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:
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);
|
||||
});
|
||||
|
||||
it('confirm that runAfterRuntimeInitialized is called', async () => {
|
||||
it('confirm that runAfterRuntimeInitialized is called', async () => {
|
||||
const mockedRunAfterRuntimeInitialized = jest
|
||||
.spyOn(instance, 'runAfterRuntimeInitialized')
|
||||
.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';
|
||||
// inspired by trump typos
|
||||
const covfefeConfig = {
|
||||
"name": "covfefe",
|
||||
"runtimes": [{
|
||||
"src": "/demo/covfefe.js",
|
||||
"name": "covfefe",
|
||||
"lang": "covfefe"
|
||||
}],
|
||||
"wonerful": "discgrace"
|
||||
name: 'covfefe',
|
||||
runtimes: [
|
||||
{
|
||||
src: '/demo/covfefe.js',
|
||||
name: 'covfefe',
|
||||
lang: 'covfefe',
|
||||
},
|
||||
],
|
||||
wonerful: 'discgrace',
|
||||
};
|
||||
|
||||
const covfefeConfigToml = `
|
||||
@@ -21,20 +25,18 @@ name = "covfefe"
|
||||
lang = "covfefe"
|
||||
`;
|
||||
|
||||
import {jest} from '@jest/globals';
|
||||
|
||||
customElements.define('py-config', PyConfig);
|
||||
|
||||
describe('PyConfig', () => {
|
||||
let instance: PyConfig;
|
||||
|
||||
const xhrMockClass = () => ({
|
||||
open : jest.fn(),
|
||||
send : jest.fn(),
|
||||
responseText : JSON.stringify(covfefeConfig)
|
||||
open: jest.fn(),
|
||||
send: jest.fn(),
|
||||
responseText: JSON.stringify(covfefeConfig),
|
||||
});
|
||||
// @ts-ignore
|
||||
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)
|
||||
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass);
|
||||
|
||||
beforeEach(() => {
|
||||
instance = new PyConfig();
|
||||
@@ -47,57 +49,57 @@ describe('PyConfig', () => {
|
||||
it('should load runtime from config and set as script src', () => {
|
||||
instance.values = covfefeConfig;
|
||||
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();
|
||||
expect(instance.values.name).toBe("pyscript");
|
||||
expect(instance.values.author_email).toBe("foo@bar.com");
|
||||
expect(instance.values.name).toBe('pyscript');
|
||||
expect(instance.values.author_email).toBe('foo@bar.com');
|
||||
expect(instance.values.pyscript?.time).not.toBeNull();
|
||||
// @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', ()=> {
|
||||
instance.setAttribute("type", "json");
|
||||
it('should load the JSON config from inline', () => {
|
||||
instance.setAttribute('type', 'json');
|
||||
instance.innerHTML = JSON.stringify(covfefeConfig);
|
||||
instance.connectedCallback();
|
||||
// @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();
|
||||
// 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', ()=> {
|
||||
instance.setAttribute("type", "json");
|
||||
instance.setAttribute("src", "/covfefe.json");
|
||||
it('should load the JSON config from src attribute', () => {
|
||||
instance.setAttribute('type', 'json');
|
||||
instance.setAttribute('src', '/covfefe.json');
|
||||
instance.connectedCallback();
|
||||
// @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();
|
||||
// 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
|
||||
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', ()=> {
|
||||
instance.setAttribute("type", "json");
|
||||
instance.innerHTML = JSON.stringify({"version": "0.2a", "wonerful": "highjacked"});
|
||||
instance.setAttribute("src", "/covfefe.json");
|
||||
it('should load the JSON config from both inline and src', () => {
|
||||
instance.setAttribute('type', 'json');
|
||||
instance.innerHTML = JSON.stringify({ version: '0.2a', wonerful: 'highjacked' });
|
||||
instance.setAttribute('src', '/covfefe.json');
|
||||
instance.connectedCallback();
|
||||
// @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();
|
||||
// config from src had an extra key "wonerful" with value "discgrace"
|
||||
// inline config had the same extra key "wonerful" with value "highjacked"
|
||||
// the merge process works for extra keys that clash as well
|
||||
// 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
|
||||
expect(instance.values.version).toBe("0.2a");
|
||||
expect(instance.values.version).toBe('0.2a');
|
||||
});
|
||||
|
||||
it('should be able to load an inline TOML config', () => {
|
||||
@@ -105,11 +107,11 @@ describe('PyConfig', () => {
|
||||
instance.innerHTML = covfefeConfigToml;
|
||||
instance.connectedCallback();
|
||||
// @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();
|
||||
// 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.wonerful).toBe("highjacked");
|
||||
expect(instance.values.version).toBe('0.1');
|
||||
expect(instance.values.wonerful).toBe('highjacked');
|
||||
});
|
||||
|
||||
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', () => {
|
||||
instance.setAttribute("type", "json");
|
||||
instance.setAttribute('type', 'json');
|
||||
instance.innerHTML = covfefeConfigToml;
|
||||
instance.connectedCallback();
|
||||
});
|
||||
|
||||
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.setAttribute("src", "/covfefe.json");
|
||||
instance.setAttribute('src', '/covfefe.json');
|
||||
instance.connectedCallback();
|
||||
});
|
||||
|
||||
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.setAttribute("src", "/covfefe.json");
|
||||
instance.setAttribute('src', '/covfefe.json');
|
||||
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 { PyRepl } from '../../src/components/pyrepl';
|
||||
|
||||
customElements.define('py-repl', PyRepl);
|
||||
@@ -13,4 +14,52 @@ describe('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