Skip to content

Commit c6f6a58

Browse files
committed
add tests for export resolution
1 parent 75ec5d9 commit c6f6a58

File tree

6 files changed

+122
-33
lines changed

6 files changed

+122
-33
lines changed

src/idom/web/utils.py

+32-26
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,50 @@ def web_module_path(name: str) -> Path:
1818

1919

2020
def resolve_module_exports_from_file(file: Path, max_depth: int) -> Set[str]:
21-
export_names, references = resolve_module_exports_from_source(file.read_text())
2221
if max_depth == 0:
23-
logger.warning(f"Unable to resolve all exports for {file}")
24-
else:
25-
for ref in references:
26-
if urlparse(ref).scheme: # is an absolute URL
27-
export_names.update(resolve_module_exports_from_url(ref, max_depth - 1))
28-
elif ref.startswith("."):
29-
path = _resolve_relative_file_path(file, ref)
30-
export_names.update(
31-
resolve_module_exports_from_file(path, max_depth - 1)
32-
)
33-
else:
34-
logger.warning(f"Did not resolve exports for unknown location {ref}")
22+
logger.warning(f"Did not resolve all exports for {file} - max depth reached")
23+
return set()
24+
elif not file.exists():
25+
logger.warning(f"Did not resolve exports for unknown file {file}")
26+
return set()
27+
28+
export_names, references = resolve_module_exports_from_source(file.read_text())
29+
30+
for ref in references:
31+
if urlparse(ref).scheme: # is an absolute URL
32+
export_names.update(resolve_module_exports_from_url(ref, max_depth - 1))
33+
else:
34+
path = _resolve_relative_file_path(file, ref)
35+
export_names.update(resolve_module_exports_from_file(path, max_depth - 1))
36+
3537
return export_names
3638

3739

3840
def resolve_module_exports_from_url(url: str, max_depth: int) -> Set[str]:
39-
export_names, references = resolve_module_exports_from_source(
40-
requests.get(url).text
41-
)
4241
if max_depth == 0:
43-
logger.warning(f"Unable to fully resolve all exports for {url}")
44-
else:
45-
for ref in references:
46-
url = _resolve_relative_url(url, ref)
47-
export_names.update(resolve_module_exports_from_url(url, max_depth - 1))
42+
logger.warning(f"Did not resolve all exports for {url} - max depth reached")
43+
return set()
44+
45+
try:
46+
text = requests.get(url).text
47+
except requests.exceptions.ConnectionError as error:
48+
reason = "" if error is None else " - {error.errno}"
49+
logger.warning("Did not resolve exports for url " + url + reason)
50+
return set()
51+
52+
export_names, references = resolve_module_exports_from_source(text)
53+
54+
for ref in references:
55+
url = _resolve_relative_url(url, ref)
56+
export_names.update(resolve_module_exports_from_url(url, max_depth - 1))
57+
4858
return export_names
4959

5060

5161
def resolve_module_exports_from_source(content: str) -> Tuple[Set[str], Set[str]]:
5262
names: Set[str] = set()
5363
references: Set[str] = set()
54-
for export in _JS_EXPORT_PATTERN.findall(
55-
# strip comments
56-
_JS_LINE_COMMENT.sub("", content)
57-
):
64+
for export in _JS_EXPORT_PATTERN.findall(content):
5865
export = export.rstrip(";").strip()
5966
# Exporting individual features
6067
if export.startswith("let "):
@@ -112,5 +119,4 @@ def _resolve_relative_url(base_url: str, rel_url: str) -> str:
112119
return f"{base_url}/{rel_url}"
113120

114121

115-
_JS_LINE_COMMENT = re.compile(r"//.*$")
116122
_JS_EXPORT_PATTERN = re.compile(r";?\s*export(?=\s+|{)(.*?(?:;|}\s*))", re.MULTILINE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export {index as Index};
2+
export * from "./one.js";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export {one as One};
2+
export * from "./two.js";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export {two as Two};
2+
export * from "https://some.external.url";

tests/test_web/js_fixtures/exports-syntax.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ export { variable1 as name10, variable2 as name11, name12 };
1616
export const { name13, name14: bar } = o;
1717

1818
// Aggregating modules
19-
export * from "source1"; // does not set the default export
20-
export * from "source2"; // does not set the default export
21-
export * as name15 from "source3"; // Draft ECMAScript® 2O21
22-
export { name16, name17 } from "source4";
23-
export { import1 as name18, import2 as name19, name20 } from "source5";
19+
export * from "https://source1.com"; // does not set the default export
20+
export * from "https://source2.com"; // does not set the default export
21+
export * as name15 from "https://source3.com"; // Draft ECMAScript® 2O21
22+
export { name16, name17 } from "https://source4.com";
23+
export { import1 as name18, import2 as name19, name20 } from "https://source5.com";

tests/test_web/test_utils.py

+79-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,90 @@
11
from pathlib import Path
22

33
import pytest
4+
import responses
45

5-
from idom.web.utils import resolve_module_exports_from_source
6+
from idom.web.utils import (
7+
resolve_module_exports_from_file,
8+
resolve_module_exports_from_source,
9+
resolve_module_exports_from_url,
10+
)
611

712

813
JS_FIXTURES_DIR = Path(__file__).parent / "js_fixtures"
914

1015

16+
@responses.activate
17+
def test_resolve_module_exports_from_file(caplog):
18+
responses.add(
19+
responses.GET,
20+
"https://some.external.url",
21+
body="export {something as ExternalUrl}",
22+
)
23+
path = JS_FIXTURES_DIR / "export-resolution" / "index.js"
24+
assert resolve_module_exports_from_file(path, 4) == {
25+
"Index",
26+
"One",
27+
"Two",
28+
"ExternalUrl",
29+
}
30+
31+
32+
def test_resolve_module_exports_from_file_log_on_max_depth(caplog):
33+
path = JS_FIXTURES_DIR / "export-resolution" / "index.js"
34+
assert resolve_module_exports_from_file(path, 0) == set()
35+
assert len(caplog.records) == 1
36+
assert caplog.records[0].message.endswith("max depth reached")
37+
38+
caplog.records.clear()
39+
40+
assert resolve_module_exports_from_file(path, 2) == {"Index", "One"}
41+
assert len(caplog.records) == 1
42+
assert caplog.records[0].message.endswith("max depth reached")
43+
44+
45+
def test_resolve_module_exports_from_file_log_on_unknown_file_location(
46+
caplog, tmp_path
47+
):
48+
file = tmp_path / "some.js"
49+
file.write_text("export * from './does-not-exist.js';")
50+
resolve_module_exports_from_file(file, 2)
51+
assert len(caplog.records) == 1
52+
assert caplog.records[0].message.startswith(
53+
"Did not resolve exports for unknown file"
54+
)
55+
56+
57+
@responses.activate
58+
def test_resolve_module_exports_from_url():
59+
responses.add(
60+
responses.GET,
61+
"https://first.url",
62+
body="export const First = 1; export * from 'https://second.url';",
63+
)
64+
responses.add(
65+
responses.GET,
66+
"https://second.url",
67+
body="export const Second = 2;",
68+
)
69+
70+
assert resolve_module_exports_from_url("https://first.url", 2) == {
71+
"First",
72+
"Second",
73+
}
74+
75+
76+
def test_resolve_module_exports_from_url_log_on_max_depth(caplog):
77+
assert resolve_module_exports_from_url("https://some.url", 0) == set()
78+
assert len(caplog.records) == 1
79+
assert caplog.records[0].message.endswith("max depth reached")
80+
81+
82+
def test_resolve_module_exports_from_url_log_on_bad_response(caplog):
83+
assert resolve_module_exports_from_url("https://some.url", 1) == set()
84+
assert len(caplog.records) == 1
85+
assert caplog.records[0].message.startswith("Did not resolve exports for url")
86+
87+
1188
@pytest.mark.parametrize(
1289
"text",
1390
[
@@ -36,5 +113,5 @@ def test_resolve_module_exports_from_source():
36113
"ClassName",
37114
}
38115
)
39-
and references == {"source1", "source2"}
116+
and references == {"https://source1.com", "https://source2.com"}
40117
)

0 commit comments

Comments
 (0)