Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 15761cd

Browse files
authored
Merge pull request #1161 from bjones1/more-tests
Improved testing 3
2 parents e37f92c + 9869cc0 commit 15761cd

5 files changed

Lines changed: 119 additions & 155 deletions

File tree

runestone/conftest.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
#
1313
# Standard library
1414
# ----------------
15-
# None.
16-
#
15+
import subprocess
16+
17+
1718
# Third-party imports
1819
# -------------------
1920
import pytest
@@ -23,20 +24,40 @@
2324
# -------------
2425
# This is necessary to bring in the shared pytest fixture.
2526
from runestone.shared_conftest import _SeleniumUtils, selenium_driver # noqa: F401
26-
from runestone.unittest_base import ModuleFixture, HOST_URL, run_webpack # noqa: F401
27+
from runestone.unittest_base import ModuleFixture, HOST_URL, IS_WINDOWS
2728

2829

2930
# Fixtures
3031
# ========
32+
# Run this once, before all tests, to update the webpacked JS.
33+
@pytest.fixture(scope="session", autouse=True)
34+
def run_webpack():
35+
# Note that Windows requires ``shell=True``, since the command to execute is ``npm.cmd``.
36+
p = subprocess.run(["npm", "run", "build"], text=True, shell=IS_WINDOWS, capture_output=True)
37+
print(p.stderr + p.stdout)
38+
assert not p.returncode
39+
40+
41+
# Provide access to the module fixture, for tests with specific needs.
3142
@pytest.fixture(scope="module")
32-
def selenium_driver_session(request):
33-
mf = ModuleFixture(request.fspath, True)
43+
def selenium_module_fixture(request):
44+
# Allow tests to specify the ``exit_status_success`` parameter passed to the ModuleFixture constructor by adding the ``@pytest.mark.exit_status_success(False)`` decorator to a test. (Marking it True instead is equivalent to the unmarked, default value.) See the `example <https://docs.pytest.org/en/stable/fixture.html#using-markers-to-pass-data-to-fixtures>`_ and the API docs <https://docs.pytest.org/en/stable/reference.html#pytest.nodes.Node.get_closest_marker>`_.
45+
exit_status_success_mark = request.node.get_closest_marker("exit_status_success")
46+
exit_status_success = True if exit_status_success_mark is None else exit_status_success_mark.args[0]
47+
48+
mf = ModuleFixture(request.fspath, exit_status_success)
3449
mf.setUpModule()
35-
yield mf.driver
50+
yield mf
3651
mf.tearDownModule()
3752

3853

39-
# Present ``_SeleniumUser`` as a fixture. To use, provide it with a ``_TestUser`` instance.
54+
# Provide access to the Selenium driver.
55+
@pytest.fixture(scope="module")
56+
def selenium_driver_session(selenium_module_fixture):
57+
return selenium_module_fixture.driver
58+
59+
60+
# Present ``_SeleniumUser`` as a fixture.
4061
@pytest.fixture
4162
def selenium_utils(selenium_driver): # noqa: F811
4263
return _SeleniumUtils(selenium_driver, HOST_URL)

runestone/lp/js/lp.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
import RunestoneBase from "../../common/js/runestonebase";
2727
import CodeMirror from "codemirror";
2828
import "codemirror/mode/gas/gas.js";
29+
import "codemirror/mode/python/python.js";
30+
import "codemirror/mode/javascript/javascript.js";
31+
import "codemirror/mode/sql/sql.js";
32+
import "codemirror/mode/clike/clike.js";
33+
import "codemirror/mode/octave/octave.js";
34+
import "codemirror/lib/codemirror.css";
2935

3036
// Constructor
3137
// ===========
@@ -39,6 +45,7 @@ class LP extends RunestoneBase {
3945
this.useRunestoneServices = opts.useRunestoneServices;
4046
// Store the DOM element (the input) for the "Test" button.
4147
this.element = opts.orig;
48+
this.containerDiv = this.element;
4249
this.divid = this.element.id;
4350
// Store the DOM element (the textarea) where compile results will be displayed.
4451
this.resultElement = $(this.element).siblings("textarea");
@@ -66,9 +73,9 @@ class LP extends RunestoneBase {
6673
// Keep track of it.
6774
that.textAreas.push(editor);
6875
});
69-
this.checkServer("lp_build");
7076
// Handle clicks to the "Save and run" button.
7177
$(this.element).click((eventObject) => that.onSaveAndRun(eventObject).then(null));
78+
this.checkServer("lp_build", true);
7279
}
7380

7481
async onSaveAndRun(_eventObject) {

runestone/lp/lp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from pathlib import Path
1818
import json
1919

20-
#
20+
2121
# Third-party imports
2222
# -------------------
2323
import pygments
@@ -30,7 +30,7 @@
3030
from CodeChat.CodeToRestSphinx import is_source_code
3131
from CodeChat.CodeToRest import get_lexer
3232

33-
#
33+
3434
# Local imports
3535
# -------------
3636
from ..common.runestonedirective import (

runestone/lp/test/test_lp.py

Lines changed: 79 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# ***********************
22
# test_lp.py - Unit tests
33
# ***********************
4-
# Require Python 3 to run tests.
5-
64
#
75
# Imports
86
# =======
@@ -12,17 +10,11 @@
1210
# Standard library
1311
# ----------------
1412
from pathlib import Path
15-
from time import sleep
16-
from unittest import TestCase
17-
1813

1914
# Third-party imports
2015
# -------------------
21-
from selenium.webdriver.common.by import By
22-
from selenium.webdriver.support.ui import WebDriverWait
23-
from selenium.webdriver.support import expected_conditions as EC
24-
25-
16+
# None.
17+
#
2618
# Local imports
2719
# -------------
2820
from runestone.lp.lp import (
@@ -36,9 +28,6 @@
3628
STUDENT_SOURCE_PATH,
3729
read_sphinx_config,
3830
)
39-
from runestone.unittest_base import module_fixture_maker, RunestoneTestCase
40-
41-
mf, setUpModule, tearDownModule = module_fixture_maker(__file__, True)
4231

4332

4433
# Mock classes
@@ -76,93 +65,98 @@ def sphinx_setup():
7665

7766
# Tests
7867
# =====
68+
#
7969
# Test the code solution removal.
80-
class Unit_Test_Lp(TestCase):
81-
# Make sure there were no unexpected warnings in the build.
82-
def test_1(self):
83-
self.assertEqual(mf.build_stderr_data.count("WARNING"), 1)
84-
85-
# Make sure that the student source files were generated.
86-
def test_1a(self):
87-
sphinx_config = read_sphinx_config()
88-
sphinx_source_path = sphinx_config["SPHINX_SOURCE_PATH"]
89-
sphinx_out_path = sphinx_config["SPHINX_OUT_PATH"]
90-
self.assertTrue(sphinx_source_path)
91-
92-
# Check that a HTML version of the source was produced.
93-
self.assertTrue((Path(sphinx_out_path) / "lp_tester.s-source.html").exists())
94-
95-
# Check that the student source has answers removed.
96-
with open(
97-
str(Path(sphinx_out_path) / STUDENT_SOURCE_PATH / "lp_tester.s"),
98-
encoding="utf-8",
99-
) as f:
100-
student_source = f.read()
101-
self.assertNotIn("_u16_b: .space 2", student_source)
102-
self.assertNotIn("mov #0xA15F, W0", student_source)
103-
104-
# Test _remove_code_solutions.
105-
def test_2(self):
106-
# Note: to avoid the tags being recognized when this file is parsed by Sphinx, they're broken apart in the strings below.
107-
self.assertEqual(
108-
_remove_code_solutions(
109-
"foo.s",
110-
"""# line 1
70+
71+
# Make sure there were no unexpected warnings in the build.
72+
def test_1(selenium_module_fixture):
73+
assert selenium_module_fixture.build_stderr_data.count("WARNING") == 1
74+
75+
76+
# Make sure that the student source files were generated.
77+
def test_1a(selenium_module_fixture):
78+
sphinx_config = read_sphinx_config()
79+
sphinx_source_path = sphinx_config["SPHINX_SOURCE_PATH"]
80+
sphinx_out_path = sphinx_config["SPHINX_OUT_PATH"]
81+
assert sphinx_source_path
82+
83+
# Check that a HTML version of the source was produced.
84+
assert (Path(sphinx_out_path) / "lp_tester.s-source.html").exists()
85+
86+
# Check that the student source has answers removed.
87+
with open(
88+
str(Path(sphinx_out_path) / STUDENT_SOURCE_PATH / "lp_tester.s"),
89+
encoding="utf-8",
90+
) as f:
91+
student_source = f.read()
92+
assert "_u16_b: .space 2" not in student_source
93+
assert "mov #0xA15F, W0" not in student_source
94+
95+
96+
# Test _remove_code_solutions.
97+
def test_2():
98+
# Note: to avoid the tags being recognized when this file is parsed by Sphinx, they're broken apart in the strings below.
99+
assert (
100+
_remove_code_solutions(
101+
"foo.s",
102+
"""# line 1
111103
# line 2
112104
# line 3
113105
# SOLUTION_"""
114-
"""BEGIN
106+
"""BEGIN
115107
Answer here.
116108
And here.
117109
Even more.
118110
# SOLUTION_"""
119-
"""END
111+
"""END
120112
# line 9
121113
# line 10
122114
# line 11
123115
# SOLUTION_"""
124-
"""BEGIN
116+
"""BEGIN
125117
More answer stuff.
126118
# SOLUTION_"""
127-
"""END
119+
"""END
128120
# line 15""",
129-
lambda start_line, end_line, file_name: "# Snip "
130-
+ str(end_line)
131-
+ "\n",
132-
),
133-
"""# line 1
121+
lambda start_line, end_line, file_name: "# Snip "
122+
+ str(end_line)
123+
+ "\n",
124+
) == """# line 1
134125
# line 2
135126
# line 3
136127
# Snip 8
137128
# line 9
138129
# line 10
139130
# line 11
140131
# Snip 14
141-
# line 15""",
142-
)
143-
144-
# Test the addition of comments to a string.
145-
def test_3(self):
146-
self.assertEqual(_add_line_comment_delimiter("1\n2", "foo.py"), "# 1\n# 2")
147-
self.assertEqual(_add_line_comment_delimiter("1\n2", "foo.c"), "// 1\n// 2")
148-
149-
# Test ``_textarea_replacement``.
150-
def test_4(self):
151-
# Check a mimimum-length replacement.
152-
self.assertEqual(
153-
_textarea_replacement(3, 4, "foo.py"), TEXTAREA_REPLACEMENT_STRING.format(4)
154-
)
155-
156-
# Check a replcement with added lines.
157-
self.assertEqual(_textarea_replacement(3, 22, "foo.py").count("\n"), 20)
158-
159-
# Test the source-read event callback.
160-
def test_5(self):
161-
src = [
162-
"""Line 1
132+
# line 15"""
133+
)
134+
135+
136+
# Test the addition of comments to a string.
137+
def test_3():
138+
assert _add_line_comment_delimiter("1\n2", "foo.py") == "# 1\n# 2"
139+
assert _add_line_comment_delimiter("1\n2", "foo.c") == "// 1\n// 2"
140+
141+
142+
# Test ``_textarea_replacement``.
143+
def test_4():
144+
# Check a mimimum-length replacement.
145+
assert (
146+
_textarea_replacement(3, 4, "foo.py") == TEXTAREA_REPLACEMENT_STRING.format(4)
147+
)
148+
149+
# Check a replacement with added lines.
150+
assert _textarea_replacement(3, 22, "foo.py").count("\n") == 20
151+
152+
153+
# Test the source-read event callback.
154+
def test_5():
155+
src = [
156+
"""Line 1
163157
Line 2
164158
SOLUTION_"""
165-
"""BEGIN
159+
"""BEGIN
166160
Line 4
167161
Line 5
168162
Line 6
@@ -171,15 +165,14 @@ def test_5(self):
171165
Line 9
172166
Line 10
173167
SOLUTION_"""
174-
"""END
168+
"""END
175169
Line 12"""
176-
]
177-
app, env = sphinx_setup()
178-
_source_read(app, env.docname, src)
179-
self.assertEqual(
180-
src,
181-
[
182-
"""Line 1
170+
]
171+
app, env = sphinx_setup()
172+
_source_read(app, env.docname, src)
173+
assert (
174+
src == [
175+
"""Line 1
183176
Line 2
184177
185178
.. raw::
@@ -191,46 +184,5 @@ def test_5(self):
191184
192185
193186
Line 12"""
194-
],
195-
)
196-
197-
198-
class Functional_Test_Lp(RunestoneTestCase):
199-
def test_1(self):
200-
self.driver.get(self.host + "/lp_tester.s.html")
201-
wait = WebDriverWait(self.driver, 10)
202-
try:
203-
wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
204-
except:
205-
text = self.driver.page_source
206-
print(text[:300])
207-
208-
snippets = self.driver.find_elements_by_class_name("code_snippet")
209-
self.assertEqual(len(snippets), 2)
210-
check_button = self.driver.find_element_by_id("e1")
211-
result_area = self.driver.find_element_by_id("lp-result")
212-
213-
# Set snippets.
214-
self.driver.execute_script(
215-
'LPList["e1"].textAreas[0].setValue("xxx"); LPList["e1"].textAreas[1].setValue("yyy");'
216-
)
217-
self.assertFalse(result_area.text)
218-
219-
# Click the test button.
220-
check_button.click()
221-
WebDriverWait(self.driver, 10).until(
222-
EC.text_to_be_present_in_element_value((By.ID, "lp-result"), "Building...")
223-
)
224-
225-
# Refresh the page. See if saved snippets are restored.
226-
self.driver.get(self.host + "/lp_tester.s.html")
227-
# Wait for script to run. I don't see a wait condition what would work, unfortunately.
228-
sleep(0.5)
229-
self.assertEqual(
230-
self.driver.execute_script('return LPList["e1"].textAreas[0].getValue();'),
231-
"xxx",
232-
)
233-
self.assertEqual(
234-
self.driver.execute_script('return LPList["e1"].textAreas[1].getValue();'),
235-
"yyy",
236-
)
187+
]
188+
)

0 commit comments

Comments
 (0)