Skip to content

Commit 1b7b150

Browse files
committed
tests, smoke-all - add tests for error: true for IPython.display error
and tweak `ensureHtmlElementContents` to use named option in YAML
1 parent c973bb9 commit 1b7b150

File tree

5 files changed

+114
-12
lines changed

5 files changed

+114
-12
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
title: test
3+
format: html
4+
_quarto:
5+
tests:
6+
html:
7+
ensureHtmlElementContents:
8+
selectors: ['div.cell-output-error']
9+
matches: ['ValueError\: Display phase error for HTML']
10+
noMatches: []
11+
---
12+
13+
With `error: true` in cell, this document should not error at rendering and Exception at IPython.display level should be shown in output.
14+
15+
By default `nbconvert` does not throw exception for error thrown by IPython display, on purpose as document output is still valid as there are other representation.
16+
17+
```{python}
18+
# First cell - create an object with a buggy _repr_html_ method
19+
class BuggyDisplay:
20+
def __init__(self):
21+
self.data = "This works fine"
22+
23+
def _repr_html_(self):
24+
# This error happens during display, not execution
25+
raise ValueError("Display phase error for HTML!")
26+
27+
def _repr_markdown_(self):
28+
# Markdown representation as fallback when HTML fails
29+
return "**Markdown fallback:** " + self.data
30+
31+
def __repr__(self):
32+
# This ensures the object has a string representation
33+
return self.data
34+
35+
# Create the object
36+
buggy = BuggyDisplay()
37+
```
38+
39+
```{python}
40+
#| error: true
41+
# Second cell - display the object (triggers error in _repr_html_)
42+
# This will show an error in the output but won't stop execution
43+
buggy
44+
```
45+
46+
```{python}
47+
# Third cell - proves execution continues
48+
print("Execution continued despite display error")
49+
```
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
title: test
3+
format: html
4+
execute:
5+
error: true
6+
_quarto:
7+
tests:
8+
html:
9+
ensureHtmlElementContents:
10+
selectors: ['div.cell-output-error']
11+
matches: ['ValueError\: Display phase error for Markdown']
12+
---
13+
14+
With `error: true` this document should not error at rendering and Exception at IPython.display level should be shown in output.
15+
16+
By default `nbconvert` does not throw exception for error thrown by IPython display, on purpose as document output is still valid as there are other representation.
17+
18+
```{python}
19+
# First cell - create an object with a buggy _repr_html_ method
20+
class BuggyDisplay:
21+
def __init__(self):
22+
self.data = "This works fine"
23+
24+
def _repr_html_(self):
25+
# This error happens during display, not execution
26+
return "<b>HTML fallback:</b> " + self.data
27+
28+
def _repr_markdown_(self):
29+
# Markdown representation as fallback when HTML fails
30+
raise ValueError("Display phase error for Markdown!")
31+
32+
def __repr__(self):
33+
# This ensures the object has a string representation
34+
return self.data
35+
36+
# Create the object
37+
buggy = BuggyDisplay()
38+
```
39+
40+
```{python}
41+
# Second cell - display the object (triggers error in _repr_html_)
42+
# This will show an error in the output but won't stop execution
43+
buggy
44+
```
45+
46+
```{python}
47+
# Third cell - proves execution continues
48+
print("Execution continued despite display error")
49+
```

tests/smoke/smoke-all.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ function resolveTestSpecs(
211211
throw new Error(`Using ensureLatexFileRegexMatches requires setting 'keep-tex: true' in file ${input}`);
212212
}
213213
}
214-
if (typeof value === "object") {
214+
215+
if (typeof value === "object" && Array.isArray(value)) {
216+
// Only use spread operator for arrays
215217
verifyFns.push(verifyMap[key](outputFile.outputPath, ...value));
216218
} else {
217219
verifyFns.push(verifyMap[key](outputFile.outputPath, value));

tests/smoke/website/draft-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ export const hasContentLinksToDrafts = (siteDir: string) => {
2626
}
2727

2828
export const hasEnvelopeLinksToDrafts = (siteDir: string) => {
29-
return ensureHtmlElementContents(join(siteDir, "index.html"), ["#quarto-sidebar", "#quarto-header", ".quarto-listing-default"], ["Draft!!"], []);
29+
return ensureHtmlElementContents(join(siteDir, "index.html"), { selectors: ["#quarto-sidebar", "#quarto-header", ".quarto-listing-default"], matches: ["Draft!!"], noMatches: [] });
3030
}
3131

3232
export const doesntHaveEnvelopeLinksToDrafts = (siteDir: string) => {
33-
return ensureHtmlElementContents(join(siteDir, "index.html"), ["#quarto-sidebar", "#quarto-header", ".quarto-listing-default"], [], ["Draft!!"]);
33+
return ensureHtmlElementContents(join(siteDir, "index.html"), { selectors: ["#quarto-sidebar", "#quarto-header", ".quarto-listing-default"], matches: [], noMatches: ["Draft!!"] });
3434
}
3535

3636
export const siteMapHasDraft = (siteDir: string) => {

tests/verify.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -345,31 +345,33 @@ export const ensureHtmlElements = (
345345
};
346346

347347
export const ensureHtmlElementContents = (
348-
file: string,
349-
selectors: string[],
350-
matches: (string | RegExp)[],
351-
noMatches: (string | RegExp)[]
348+
file: string,
349+
options : {
350+
selectors: string[],
351+
matches: (string | RegExp)[],
352+
noMatches?: (string | RegExp)[]
353+
}
352354
) => {
353355
return {
354356
name: "Inspecting HTML for Selector Contents",
355357
verify: async (_output: ExecuteOutput[]) => {
356358
const htmlInput = await Deno.readTextFile(file);
357359
const doc = new DOMParser().parseFromString(htmlInput, "text/html")!;
358-
selectors.forEach((sel) => {
360+
options.selectors.forEach((sel) => {
359361
const el = doc.querySelector(sel);
360362
if (el !== null) {
361363
const contents = el.innerText;
362-
matches.forEach((regex) => {
364+
options.matches.forEach((regex) => {
363365
assert(
364366
asRegexp(regex).test(contents),
365-
`Required match ${String(regex)} is missing from selector ${sel}.`,
367+
`Required match ${String(regex)} is missing from selector ${sel} content: ${contents}.`,
366368
);
367369
});
368370

369-
noMatches.forEach((regex) => {
371+
options.noMatches?.forEach((regex) => {
370372
assert(
371373
!asRegexp(regex).test(contents),
372-
`Unexpected match ${String(regex)} is present from selector ${sel}.`,
374+
`Unexpected match ${String(regex)} is present from selector ${sel} content: ${contents}.`,
373375
);
374376
});
375377

0 commit comments

Comments
 (0)