donobu 5.39.1 → 5.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,14 +64,14 @@ function ansiToHtml(str) {
64
64
  * Read a few lines of source around `targetLine` (1-based) and return an HTML
65
65
  * snippet block, or null if the file is unreadable.
66
66
  */
67
- function readSourceSnippet(file, targetLine) {
67
+ function readSourceSnippet(file, targetLine, wrapperClass = 'native-step-snippet') {
68
68
  try {
69
69
  const source = (0, fs_1.readFileSync)(file, 'utf8');
70
70
  const lines = source.split('\n');
71
71
  const context = 2;
72
72
  const start = Math.max(0, targetLine - 1 - context);
73
73
  const end = Math.min(lines.length - 1, targetLine - 1 + context);
74
- let html = '<div class="native-step-snippet">';
74
+ let html = `<div class="${wrapperClass}">`;
75
75
  for (let i = start; i <= end; i++) {
76
76
  const lineNum = i + 1;
77
77
  const isTarget = lineNum === targetLine;
@@ -1515,6 +1515,26 @@ function renderHtml(report, triage, outputDir) {
1515
1515
  const lastResult = test.results.at(-1);
1516
1516
  const displayFileName = test.file.split('/').pop() ?? test.file;
1517
1517
  let detailsHtml = '';
1518
+ // 0. Skip reason — surface the description and call site from
1519
+ // test.skip()/test.fixme() so a skipped test explains itself.
1520
+ if (test.status === 'skipped') {
1521
+ const skipAnnotation = test.annotations.find((a) => a.type === 'skip' || a.type === 'fixme');
1522
+ const label = skipAnnotation?.type === 'fixme' ? 'Fixme reason' : 'Skip reason';
1523
+ const description = skipAnnotation?.description?.trim();
1524
+ const body = description
1525
+ ? esc(description)
1526
+ : 'No description was provided when this test was skipped. The reason may be implicit in the surrounding test code or condition.';
1527
+ let locationHtml = '';
1528
+ let snippetHtml = '';
1529
+ const loc = skipAnnotation?.location;
1530
+ if (loc?.file) {
1531
+ const fileBase = (0, path_1.basename)(loc.file);
1532
+ locationHtml = `<div class="skip-location">at <span class="skip-location-ref">${esc(fileBase)}:${loc.line}</span></div>`;
1533
+ snippetHtml =
1534
+ readSourceSnippet(loc.file, loc.line, 'source-snippet') ?? '';
1535
+ }
1536
+ detailsHtml += `<div class="detail-section"><div class="detail-label">${label}</div><div class="detail-objective">${body}</div>${locationHtml}${snippetHtml}</div>`;
1537
+ }
1518
1538
  // 1. AI analysis — why it failed (the headline)
1519
1539
  if (test.plan) {
1520
1540
  detailsHtml += renderFailureSummary(test.plan);
@@ -1824,6 +1844,11 @@ details.native-step>summary::-webkit-details-marker{display:none}
1824
1844
  details.native-step[open]>summary .native-step-chevron{transform:rotate(90deg)}
1825
1845
  .native-step-error{font-size:11px;font-family:var(--mono);padding:4px 0 2px 44px;margin:0;white-space:pre-wrap;word-break:break-word;color:var(--text-muted)}
1826
1846
  .native-step-snippet{font-size:11px;font-family:var(--mono);margin:4px 0 2px 44px;overflow:hidden}
1847
+ .skip-location{font-size:11px;color:var(--text-muted);margin-top:8px;font-family:var(--mono)}
1848
+ .skip-location-ref{color:var(--text)}
1849
+ .source-snippet{font-size:11px;font-family:var(--mono);margin:6px 0 0 0;overflow:hidden;border:1px solid var(--border-subtle);border-radius:var(--radius);padding:6px 0;background:var(--bg)}
1850
+ .source-snippet .snippet-line--target{background:rgba(255,127,58,.10)}
1851
+ .source-snippet .snippet-line--target .snippet-linenum{color:var(--orange,#FF7F3A)}
1827
1852
  .native-step-children{display:flex;flex-direction:column;margin:4px 0 0 10px;border-left:1px solid var(--border-subtle);padding-left:8px}
1828
1853
  .native-step-children>.filmstrip-step{padding-left:8px}
1829
1854
 
@@ -64,14 +64,14 @@ function ansiToHtml(str) {
64
64
  * Read a few lines of source around `targetLine` (1-based) and return an HTML
65
65
  * snippet block, or null if the file is unreadable.
66
66
  */
67
- function readSourceSnippet(file, targetLine) {
67
+ function readSourceSnippet(file, targetLine, wrapperClass = 'native-step-snippet') {
68
68
  try {
69
69
  const source = (0, fs_1.readFileSync)(file, 'utf8');
70
70
  const lines = source.split('\n');
71
71
  const context = 2;
72
72
  const start = Math.max(0, targetLine - 1 - context);
73
73
  const end = Math.min(lines.length - 1, targetLine - 1 + context);
74
- let html = '<div class="native-step-snippet">';
74
+ let html = `<div class="${wrapperClass}">`;
75
75
  for (let i = start; i <= end; i++) {
76
76
  const lineNum = i + 1;
77
77
  const isTarget = lineNum === targetLine;
@@ -1515,6 +1515,26 @@ function renderHtml(report, triage, outputDir) {
1515
1515
  const lastResult = test.results.at(-1);
1516
1516
  const displayFileName = test.file.split('/').pop() ?? test.file;
1517
1517
  let detailsHtml = '';
1518
+ // 0. Skip reason — surface the description and call site from
1519
+ // test.skip()/test.fixme() so a skipped test explains itself.
1520
+ if (test.status === 'skipped') {
1521
+ const skipAnnotation = test.annotations.find((a) => a.type === 'skip' || a.type === 'fixme');
1522
+ const label = skipAnnotation?.type === 'fixme' ? 'Fixme reason' : 'Skip reason';
1523
+ const description = skipAnnotation?.description?.trim();
1524
+ const body = description
1525
+ ? esc(description)
1526
+ : 'No description was provided when this test was skipped. The reason may be implicit in the surrounding test code or condition.';
1527
+ let locationHtml = '';
1528
+ let snippetHtml = '';
1529
+ const loc = skipAnnotation?.location;
1530
+ if (loc?.file) {
1531
+ const fileBase = (0, path_1.basename)(loc.file);
1532
+ locationHtml = `<div class="skip-location">at <span class="skip-location-ref">${esc(fileBase)}:${loc.line}</span></div>`;
1533
+ snippetHtml =
1534
+ readSourceSnippet(loc.file, loc.line, 'source-snippet') ?? '';
1535
+ }
1536
+ detailsHtml += `<div class="detail-section"><div class="detail-label">${label}</div><div class="detail-objective">${body}</div>${locationHtml}${snippetHtml}</div>`;
1537
+ }
1518
1538
  // 1. AI analysis — why it failed (the headline)
1519
1539
  if (test.plan) {
1520
1540
  detailsHtml += renderFailureSummary(test.plan);
@@ -1824,6 +1844,11 @@ details.native-step>summary::-webkit-details-marker{display:none}
1824
1844
  details.native-step[open]>summary .native-step-chevron{transform:rotate(90deg)}
1825
1845
  .native-step-error{font-size:11px;font-family:var(--mono);padding:4px 0 2px 44px;margin:0;white-space:pre-wrap;word-break:break-word;color:var(--text-muted)}
1826
1846
  .native-step-snippet{font-size:11px;font-family:var(--mono);margin:4px 0 2px 44px;overflow:hidden}
1847
+ .skip-location{font-size:11px;color:var(--text-muted);margin-top:8px;font-family:var(--mono)}
1848
+ .skip-location-ref{color:var(--text)}
1849
+ .source-snippet{font-size:11px;font-family:var(--mono);margin:6px 0 0 0;overflow:hidden;border:1px solid var(--border-subtle);border-radius:var(--radius);padding:6px 0;background:var(--bg)}
1850
+ .source-snippet .snippet-line--target{background:rgba(255,127,58,.10)}
1851
+ .source-snippet .snippet-line--target .snippet-linenum{color:var(--orange,#FF7F3A)}
1827
1852
  .native-step-children{display:flex;flex-direction:column;margin:4px 0 0 10px;border-left:1px solid var(--border-subtle);padding-left:8px}
1828
1853
  .native-step-children>.filmstrip-step{padding-left:8px}
1829
1854
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.39.1",
3
+ "version": "5.40.0",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",